Modelo

source("../../lib/som-utils.R")

Attaching package: 'dplyr'
The following objects are masked from 'package:stats':

    filter, lag
The following objects are masked from 'package:base':

    intersect, setdiff, setequal, union
source("../../lib/maps-utils.R")
Linking to GEOS 3.8.0, GDAL 3.0.4, PROJ 6.3.1

Carga del modelo desde disco

mpr.set_base_path_analysis()
model <- mpr.load_model("som-331.rds.xz")
summary(model)
SOM of size 10x10 with a hexagonal topology and a bubble neighbourhood function.
The number of data layers is 1.
Distance measure(s) used: sumofsquares.
Training data included: 94881 objects.
Mean distance to the closest unit in the map: 0.182.
plot(model, type="changes")

Carga del dataset de entrada

df <- mpr.load_data("datos_mes.csv.xz")
df
summary(df)
 id_estacion           fecha             fecha_cnt           tmax      
 Length:94881       Length:94881       Min.   : 1.000   Min.   :-53.0  
 Class :character   Class :character   1st Qu.: 4.000   1st Qu.:148.0  
 Mode  :character   Mode  :character   Median : 6.000   Median :198.0  
                                       Mean   : 6.497   Mean   :200.2  
                                       3rd Qu.: 9.000   3rd Qu.:255.0  
                                       Max.   :12.000   Max.   :403.0  
      tmin             precip           nevada           prof_nieve      
 Min.   :-121.00   Min.   :  0.00   Min.   :0.000000   Min.   :   0.000  
 1st Qu.:  53.00   1st Qu.:  3.00   1st Qu.:0.000000   1st Qu.:   0.000  
 Median :  98.00   Median : 10.00   Median :0.000000   Median :   0.000  
 Mean   :  98.86   Mean   : 16.25   Mean   :0.000295   Mean   :   0.467  
 3rd Qu.: 148.00   3rd Qu.: 22.00   3rd Qu.:0.000000   3rd Qu.:   0.000  
 Max.   : 254.00   Max.   :422.00   Max.   :6.000000   Max.   :1834.000  
    longitud        latitud            altitud      
 Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.:38.28   1st Qu.: -5.6417   1st Qu.:  42.0  
 Median :40.82   Median : -3.4500   Median : 247.0  
 Mean   :39.66   Mean   : -3.4350   Mean   : 418.5  
 3rd Qu.:42.08   3rd Qu.:  0.4914   3rd Qu.: 656.0  
 Max.   :43.57   Max.   :  4.2156   Max.   :2535.0  

Carga de los mapas

world <- ne_countries(scale = "medium", returnclass = "sf")
spain <- subset(world, admin == "Spain")

Mapa de densidad

plot(model, type="count", shape = "straight", palette.name = mpr.degrade.bleu)

NĂºmero de elementos en cada celda:

nb <- table(model$unit.classif)
print(nb)

   1    2    3    4    5    6    7    8    9   10   11   12   13   14   15   16 
1008 1492  799 1573  847 1504 1762 1192 1694 1439  232 1217 1110 1060  861 1432 
  17   18   19   20   21   22   23   24   25   26   27   28   29   30   31   32 
1218 1446  950 1653 1489 1163 1376 1351 1225  910 1116 1563  928  512 1197 1345 
  33   34   35   36   37   38   39   40   41   42   43   44   45   46   47   48 
 616 1467 1207  515  611  904 1227 1200 1659 1413 1308 1082  752  405  888 1423 
  49   50   51   52   53   54   55   56   57   58   59   60   61   62   63   64 
1230 1078 1162  894  991 1090 1044  535  767 1081  777 1135 1349 1475  266  900 
  65   66   67   68   69   70   71   72   73   74   75   76   77   78   79   80 
 594  384  486  937 1454 1322  996 1158  917  811  668  372  295  697 1061 1320 
  81   82   83   84   85   86   87   88   89   90   91   92   93   94   95   96 
1255  687  613  450  291  180  200  516 1175 1625 1009  258  310  147   97   26 
  97   98   99  100 
  76  178  204  997 

ComprobaciĂ³n de nodos vacĂ­os:

dim_model <- 10*10;
len_nb = length(nb);
empty_nodes <- dim_model != len_nb;
if (empty_nodes) {
  print(paste("[Warning] Existen nodos vacĂ­os: ", len_nb, "/", dim_model))
}

Mapa de distancia entre vecinos

plot(model, type="dist.neighbours", shape = "straight")

Influencia de las variables

model_colnames = c("fecha_cnt", "tmax", "tmin", "precip")
model_ncol = length(model_colnames)

Mapa de variables.

plot(model, shape = "straight")

Mapa de calor por variable

par(mfrow=c(3,4))
for (j in 1:model_ncol) {
  plot(model, type="property", property=getCodes(model,1)[,j],
    palette.name=mpr.coolBlueHotRed,
    main=model_colnames[j],
    cex=0.5, shape = "straight")
}

CorrelaciĂ³n para cada columna del vector de nodos

if (!empty_nodes) {
  cor <- apply(getCodes(model,1), 2, mpr.weighted.correlation, w=nb, som=model)
  print(cor)
}
        fecha_cnt       tmax       tmin      precip
[1,]  0.907868021  0.2963817  0.3448915 0.002281044
[2,] -0.003652048 -0.8673221 -0.8556988 0.484260167

RepresentaciĂ³n de cada variable en un mapa de factores:

if (!empty_nodes) {
  par(mfrow=c(1,1))
  plot(cor[1,], cor[2,], xlim=c(-1,1), ylim=c(-1,1), type="n")
  lines(c(-1,1),c(0,0))
  lines(c(0,0),c(-1,1))
  text(cor[1,], cor[2,], labels=model_colnames, cex=0.75)
  symbols(0,0,circles=1,inches=F,add=T)
}

Importancia de cada variable - varianza ponderada por el tamaño de la celda:

if (!empty_nodes) {
  sigma2 <- sqrt(apply(getCodes(model,1),2,function(x,effectif)
     {m<-sum(effectif*(x-weighted.mean(x,effectif))^2)/(sum(effectif)-1)},
     effectif=nb))
  print(sort(sigma2,decreasing=T))
}
fecha_cnt      tmax    precip      tmin 
0.9821812 0.9781205 0.9746372 0.9732892 

Clustering

if (!empty_nodes) {
  hac <- mpr.hac(model, nb)
}

VisualizaciĂ³n de 3 clĂºsteres:

if (!empty_nodes) {
  plot(hac, hang=-1, labels=F)
  rect.hclust(hac, k=3)
}

VisualizaciĂ³n de los clĂºsters en el mapa

A quĂ© clĂºster pertenece cada nodo del mapa de kohonen:

if (!empty_nodes) {
  groups <- cutree(hac, k=3)
  plot(model, type="mapping",
    bgcol=c("steelblue1","sienna1","yellowgreen","red","blue","yellow","purple","green","white","#1f77b4", '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2')[groups],
    shape = "straight", labels = "")
  add.cluster.boundaries(model, clustering=groups)
}

AnĂ¡lisis de las observaciones de cada cluster

if (!empty_nodes) {
  # Asignamos a cada registro su clĂºster
  df$cluster <- groups[model$unit.classif]
}

Nuevos dataframes por cluster

if (!empty_nodes) {
  # Creo nuevos dataframes, uno por cada clĂºster.
  df.cluster01 <- subset(df, cluster==1)
  df.cluster02 <- subset(df, cluster==2)
  df.cluster03 <- subset(df, cluster==3)

  # Extraigo del dataframe las features.
  df.cluster01 <- select(df.cluster01, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster02 <- select(df.cluster02, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster03 <- select(df.cluster03, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
}
if (!empty_nodes) summary(df.cluster01)
   fecha_cnt           tmax            tmin           precip          nevada 
 Min.   : 1.000   Min.   :158.0   Min.   : 54.0   Min.   : 0.00   Min.   :0  
 1st Qu.: 6.000   1st Qu.:239.0   1st Qu.:135.0   1st Qu.: 1.00   1st Qu.:0  
 Median : 7.000   Median :267.0   Median :157.0   Median : 5.00   Median :0  
 Mean   : 7.428   Mean   :268.3   Mean   :158.6   Mean   : 9.33   Mean   :0  
 3rd Qu.: 9.000   3rd Qu.:296.0   3rd Qu.:182.0   3rd Qu.:13.00   3rd Qu.:0  
 Max.   :12.000   Max.   :403.0   Max.   :254.0   Max.   :91.00   Max.   :0  
   prof_nieve          longitud        latitud            altitud      
 Min.   : 0.00000   Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.: 0.00000   1st Qu.:37.19   1st Qu.: -6.0556   1st Qu.:  32.0  
 Median : 0.00000   Median :39.95   Median : -3.6781   Median :  87.0  
 Mean   : 0.00356   Mean   :38.56   Mean   : -4.2132   Mean   : 287.8  
 3rd Qu.: 0.00000   3rd Qu.:41.65   3rd Qu.:  0.3664   3rd Qu.: 540.0  
 Max.   :35.00000   Max.   :43.57   Max.   :  4.2156   Max.   :2371.0  
if (!empty_nodes) summary(df.cluster02)
   fecha_cnt           tmax            tmin             precip      
 Min.   : 1.000   Min.   :-53.0   Min.   :-121.00   Min.   :  0.00  
 1st Qu.: 2.000   1st Qu.:124.0   1st Qu.:  29.00   1st Qu.:  6.00  
 Median : 4.000   Median :158.0   Median :  61.00   Median : 14.00  
 Mean   : 5.728   Mean   :153.6   Mean   :  57.22   Mean   : 18.87  
 3rd Qu.:10.000   3rd Qu.:187.0   3rd Qu.:  88.00   3rd Qu.: 27.00  
 Max.   :12.000   Max.   :278.0   Max.   : 180.00   Max.   :126.00  
     nevada           prof_nieve           longitud        latitud        
 Min.   :0.000000   Min.   :   0.0000   Min.   :27.82   Min.   :-17.8889  
 1st Qu.:0.000000   1st Qu.:   0.0000   1st Qu.:39.47   1st Qu.: -5.3456  
 Median :0.000000   Median :   0.0000   Median :41.11   Median : -2.9056  
 Mean   :0.000513   Mean   :   0.7536   Mean   :40.40   Mean   : -2.8507  
 3rd Qu.:0.000000   3rd Qu.:   0.0000   3rd Qu.:42.33   3rd Qu.:  0.4942  
 Max.   :6.000000   Max.   :1834.0000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.:  64.0  
 Median : 412.0  
 Mean   : 509.2  
 3rd Qu.: 735.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster03)
   fecha_cnt          tmax            tmin            precip           nevada 
 Min.   : 1.00   Min.   :-15.0   Min.   :-75.00   Min.   : 52.00   Min.   :0  
 1st Qu.:10.00   1st Qu.:117.0   1st Qu.: 52.00   1st Qu.: 71.00   1st Qu.:0  
 Median :11.00   Median :142.0   Median : 76.00   Median : 89.00   Median :0  
 Mean   :10.34   Mean   :138.5   Mean   : 71.91   Mean   : 97.53   Mean   :0  
 3rd Qu.:12.00   3rd Qu.:164.0   3rd Qu.: 96.00   3rd Qu.:113.75   3rd Qu.:0  
 Max.   :12.00   Max.   :350.0   Max.   :223.00   Max.   :422.00   Max.   :0  
   prof_nieve        longitud        latitud           altitud      
 Min.   :  0.00   Min.   :27.82   Min.   :-17.889   Min.   :   1.0  
 1st Qu.:  0.00   1st Qu.:40.80   1st Qu.: -8.411   1st Qu.:  44.0  
 Median :  0.00   Median :42.44   Median : -4.010   Median : 251.0  
 Mean   :  1.96   Mean   :41.44   Mean   : -4.580   Mean   : 491.5  
 3rd Qu.:  0.00   3rd Qu.:43.31   3rd Qu.: -1.787   3rd Qu.: 418.8  
 Max.   :607.00   Max.   :43.57   Max.   :  4.216   Max.   :2535.0  

NĂºmero de elementos en cada clĂºster

if (!empty_nodes) {
  df.clusters.dim <- c(dim(df.cluster01)[1], dim(df.cluster02)[1], dim(df.cluster03)[1])
  barplot(df.clusters.dim,
          names.arg = c("cluster01", "cluster02", "cluster03"),
          col = "steelblue1")
}

DistribuciĂ³n de los datos

if (!empty_nodes) mpr.hist(df.cluster01)

if (!empty_nodes) mpr.hist(df.cluster02)

if (!empty_nodes) mpr.hist(df.cluster03)

if (!empty_nodes) mpr.boxplot(df.cluster01)
if (!empty_nodes) mpr.boxplot(df.cluster02)

if (!empty_nodes) mpr.boxplot(df.cluster03)

LocalizaciĂ³n geogrĂ¡fica de las estaciones de medida de cada cluster

# Agrupa por longitud y latitud para rellenar el mapa con menos datos.
if (!empty_nodes) {
  df.cluster01.grouped <- mpr.group_by_geo(df.cluster01)
  df.cluster02.grouped <- mpr.group_by_geo(df.cluster02)
  df.cluster03.grouped <- mpr.group_by_geo(df.cluster03)
}
if (!empty_nodes) mpr.draw_map(spain, df.cluster01.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster02.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster03.grouped)

VisualizaciĂ³n de 4 clĂºsteres:

if (!empty_nodes) {
  plot(hac, hang=-1, labels=F)
  rect.hclust(hac, k=4)
}

VisualizaciĂ³n de los clĂºsters en el mapa

A quĂ© clĂºster pertenece cada nodo del mapa de kohonen:

if (!empty_nodes) {
  groups <- cutree(hac, k=4)
  plot(model, type="mapping",
    bgcol=c("steelblue1","sienna1","yellowgreen","red","blue","yellow","purple","green","white","#1f77b4", '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2')[groups],
    shape = "straight", labels = "")
  add.cluster.boundaries(model, clustering=groups)
}

AnĂ¡lisis de las observaciones de cada cluster

if (!empty_nodes) {
  # Asignamos a cada registro su clĂºster
  df$cluster <- groups[model$unit.classif]
}

Nuevos dataframes por cluster

if (!empty_nodes) {
  # Creo nuevos dataframes, uno por cada clĂºster.
  df.cluster01 <- subset(df, cluster==1)
  df.cluster02 <- subset(df, cluster==2)
  df.cluster03 <- subset(df, cluster==3)
  df.cluster04 <- subset(df, cluster==4)

  # Extraigo del dataframe las features.
  df.cluster01 <- select(df.cluster01, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster02 <- select(df.cluster02, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster03 <- select(df.cluster03, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster04 <- select(df.cluster04, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
}
if (!empty_nodes) summary(df.cluster01)
   fecha_cnt           tmax            tmin           precip          nevada 
 Min.   : 1.000   Min.   :158.0   Min.   : 54.0   Min.   : 0.00   Min.   :0  
 1st Qu.: 6.000   1st Qu.:239.0   1st Qu.:135.0   1st Qu.: 1.00   1st Qu.:0  
 Median : 7.000   Median :267.0   Median :157.0   Median : 5.00   Median :0  
 Mean   : 7.428   Mean   :268.3   Mean   :158.6   Mean   : 9.33   Mean   :0  
 3rd Qu.: 9.000   3rd Qu.:296.0   3rd Qu.:182.0   3rd Qu.:13.00   3rd Qu.:0  
 Max.   :12.000   Max.   :403.0   Max.   :254.0   Max.   :91.00   Max.   :0  
   prof_nieve          longitud        latitud            altitud      
 Min.   : 0.00000   Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.: 0.00000   1st Qu.:37.19   1st Qu.: -6.0556   1st Qu.:  32.0  
 Median : 0.00000   Median :39.95   Median : -3.6781   Median :  87.0  
 Mean   : 0.00356   Mean   :38.56   Mean   : -4.2132   Mean   : 287.8  
 3rd Qu.: 0.00000   3rd Qu.:41.65   3rd Qu.:  0.3664   3rd Qu.: 540.0  
 Max.   :35.00000   Max.   :43.57   Max.   :  4.2156   Max.   :2371.0  
if (!empty_nodes) summary(df.cluster02)
   fecha_cnt         tmax            tmin            precip      
 Min.   :1.00   Min.   :-53.0   Min.   :-121.0   Min.   :  0.00  
 1st Qu.:2.00   1st Qu.:126.0   1st Qu.:  28.0   1st Qu.:  6.00  
 Median :3.00   Median :159.0   Median :  59.0   Median : 14.00  
 Mean   :2.95   Mean   :154.6   Mean   :  55.5   Mean   : 19.04  
 3rd Qu.:4.00   3rd Qu.:187.0   3rd Qu.:  86.0   3rd Qu.: 27.00  
 Max.   :8.00   Max.   :278.0   Max.   : 180.0   Max.   :126.00  
     nevada           prof_nieve          longitud        latitud        
 Min.   :0.000000   Min.   :   0.000   Min.   :27.82   Min.   :-17.8889  
 1st Qu.:0.000000   1st Qu.:   0.000   1st Qu.:39.01   1st Qu.: -5.4981  
 Median :0.000000   Median :   0.000   Median :41.11   Median : -2.9056  
 Mean   :0.000531   Mean   :   1.046   Mean   :40.35   Mean   : -2.9248  
 3rd Qu.:0.000000   3rd Qu.:   0.000   3rd Qu.:42.33   3rd Qu.:  0.4914  
 Max.   :6.000000   Max.   :1834.000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.:  58.0  
 Median : 359.0  
 Mean   : 488.5  
 3rd Qu.: 704.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster03)
   fecha_cnt          tmax            tmin             precip     
 Min.   : 7.00   Min.   :-24.0   Min.   :-110.00   Min.   : 0.00  
 1st Qu.:10.00   1st Qu.:120.0   1st Qu.:  32.00   1st Qu.: 6.00  
 Median :11.00   Median :156.0   Median :  64.00   Median :15.00  
 Mean   :11.02   Mean   :151.7   Mean   :  60.49   Mean   :18.53  
 3rd Qu.:12.00   3rd Qu.:186.0   3rd Qu.:  91.00   3rd Qu.:28.00  
 Max.   :12.00   Max.   :263.0   Max.   : 165.00   Max.   :64.00  
     nevada            prof_nieve          longitud        latitud        
 Min.   :0.0000000   Min.   :  0.0000   Min.   :27.92   Min.   :-17.7550  
 1st Qu.:0.0000000   1st Qu.:  0.0000   1st Qu.:39.49   1st Qu.: -4.8500  
 Median :0.0000000   Median :  0.0000   Median :41.11   Median : -2.6544  
 Mean   :0.0004787   Mean   :  0.1972   Mean   :40.49   Mean   : -2.7096  
 3rd Qu.:0.0000000   3rd Qu.:  0.0000   3rd Qu.:42.29   3rd Qu.:  0.5706  
 Max.   :3.0000000   Max.   :338.0000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.:  71.0  
 Median : 445.0  
 Mean   : 548.7  
 3rd Qu.: 779.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster04)
   fecha_cnt          tmax            tmin            precip           nevada 
 Min.   : 1.00   Min.   :-15.0   Min.   :-75.00   Min.   : 52.00   Min.   :0  
 1st Qu.:10.00   1st Qu.:117.0   1st Qu.: 52.00   1st Qu.: 71.00   1st Qu.:0  
 Median :11.00   Median :142.0   Median : 76.00   Median : 89.00   Median :0  
 Mean   :10.34   Mean   :138.5   Mean   : 71.91   Mean   : 97.53   Mean   :0  
 3rd Qu.:12.00   3rd Qu.:164.0   3rd Qu.: 96.00   3rd Qu.:113.75   3rd Qu.:0  
 Max.   :12.00   Max.   :350.0   Max.   :223.00   Max.   :422.00   Max.   :0  
   prof_nieve        longitud        latitud           altitud      
 Min.   :  0.00   Min.   :27.82   Min.   :-17.889   Min.   :   1.0  
 1st Qu.:  0.00   1st Qu.:40.80   1st Qu.: -8.411   1st Qu.:  44.0  
 Median :  0.00   Median :42.44   Median : -4.010   Median : 251.0  
 Mean   :  1.96   Mean   :41.44   Mean   : -4.580   Mean   : 491.5  
 3rd Qu.:  0.00   3rd Qu.:43.31   3rd Qu.: -1.787   3rd Qu.: 418.8  
 Max.   :607.00   Max.   :43.57   Max.   :  4.216   Max.   :2535.0  

NĂºmero de elementos en cada clĂºster

if (!empty_nodes) {
  df.clusters.dim <- c(dim(df.cluster01)[1], dim(df.cluster02)[1], dim(df.cluster03)[1], dim(df.cluster04)[1])
  barplot(df.clusters.dim,
          names.arg = c("cluster01", "cluster02", "cluster03", "cluster04"),
          col = "steelblue1")
}

DistribuciĂ³n de los datos

if (!empty_nodes) mpr.hist(df.cluster01)

if (!empty_nodes) mpr.hist(df.cluster02)

if (!empty_nodes) mpr.hist(df.cluster03)

if (!empty_nodes) mpr.hist(df.cluster04)

if (!empty_nodes) mpr.boxplot(df.cluster01)
if (!empty_nodes) mpr.boxplot(df.cluster02)

if (!empty_nodes) mpr.boxplot(df.cluster03)

if (!empty_nodes) mpr.boxplot(df.cluster04)

LocalizaciĂ³n geogrĂ¡fica de las estaciones de medida de cada cluster

# Agrupa por longitud y latitud para rellenar el mapa con menos datos.
if (!empty_nodes) {
  df.cluster01.grouped <- mpr.group_by_geo(df.cluster01)
  df.cluster02.grouped <- mpr.group_by_geo(df.cluster02)
  df.cluster03.grouped <- mpr.group_by_geo(df.cluster03)
  df.cluster04.grouped <- mpr.group_by_geo(df.cluster04)
}
if (!empty_nodes) mpr.draw_map(spain, df.cluster01.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster02.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster03.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster04.grouped)

VisualizaciĂ³n de 5 clĂºsteres:

if (!empty_nodes) {
  plot(hac, hang=-1, labels=F)
  rect.hclust(hac, k=5)
}

VisualizaciĂ³n de los clĂºsters en el mapa

A quĂ© clĂºster pertenece cada nodo del mapa de kohonen:

if (!empty_nodes) {
  groups <- cutree(hac, k=5)
  plot(model, type="mapping",
    bgcol=c("steelblue1","sienna1","yellowgreen","red","blue","yellow","purple","green","white","#1f77b4", '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2')[groups],
    shape = "straight", labels = "")
  add.cluster.boundaries(model, clustering=groups)
}

AnĂ¡lisis de las observaciones de cada cluster

if (!empty_nodes) {
  # Asignamos a cada registro su clĂºster
  df$cluster <- groups[model$unit.classif]
}

Nuevos dataframes por cluster

if (!empty_nodes) {
  # Creo nuevos dataframes, uno por cada clĂºster.
  df.cluster01 <- subset(df, cluster==1)
  df.cluster02 <- subset(df, cluster==2)
  df.cluster03 <- subset(df, cluster==3)
  df.cluster04 <- subset(df, cluster==4)
  df.cluster05 <- subset(df, cluster==5)

  # Extraigo del dataframe las features.
  df.cluster01 <- select(df.cluster01, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster02 <- select(df.cluster02, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster03 <- select(df.cluster03, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster04 <- select(df.cluster04, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster05 <- select(df.cluster05, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
}
if (!empty_nodes) summary(df.cluster01)
   fecha_cnt           tmax            tmin           precip          nevada 
 Min.   : 1.000   Min.   :158.0   Min.   : 54.0   Min.   : 0.00   Min.   :0  
 1st Qu.: 6.000   1st Qu.:239.0   1st Qu.:135.0   1st Qu.: 1.00   1st Qu.:0  
 Median : 7.000   Median :267.0   Median :157.0   Median : 5.00   Median :0  
 Mean   : 7.428   Mean   :268.3   Mean   :158.6   Mean   : 9.33   Mean   :0  
 3rd Qu.: 9.000   3rd Qu.:296.0   3rd Qu.:182.0   3rd Qu.:13.00   3rd Qu.:0  
 Max.   :12.000   Max.   :403.0   Max.   :254.0   Max.   :91.00   Max.   :0  
   prof_nieve          longitud        latitud            altitud      
 Min.   : 0.00000   Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.: 0.00000   1st Qu.:37.19   1st Qu.: -6.0556   1st Qu.:  32.0  
 Median : 0.00000   Median :39.95   Median : -3.6781   Median :  87.0  
 Mean   : 0.00356   Mean   :38.56   Mean   : -4.2132   Mean   : 287.8  
 3rd Qu.: 0.00000   3rd Qu.:41.65   3rd Qu.:  0.3664   3rd Qu.: 540.0  
 Max.   :35.00000   Max.   :43.57   Max.   :  4.2156   Max.   :2371.0  
if (!empty_nodes) summary(df.cluster02)
   fecha_cnt         tmax            tmin            precip      
 Min.   :1.00   Min.   :-53.0   Min.   :-121.0   Min.   :  0.00  
 1st Qu.:2.00   1st Qu.:126.0   1st Qu.:  28.0   1st Qu.:  6.00  
 Median :3.00   Median :159.0   Median :  59.0   Median : 14.00  
 Mean   :2.95   Mean   :154.6   Mean   :  55.5   Mean   : 19.04  
 3rd Qu.:4.00   3rd Qu.:187.0   3rd Qu.:  86.0   3rd Qu.: 27.00  
 Max.   :8.00   Max.   :278.0   Max.   : 180.0   Max.   :126.00  
     nevada           prof_nieve          longitud        latitud        
 Min.   :0.000000   Min.   :   0.000   Min.   :27.82   Min.   :-17.8889  
 1st Qu.:0.000000   1st Qu.:   0.000   1st Qu.:39.01   1st Qu.: -5.4981  
 Median :0.000000   Median :   0.000   Median :41.11   Median : -2.9056  
 Mean   :0.000531   Mean   :   1.046   Mean   :40.35   Mean   : -2.9248  
 3rd Qu.:0.000000   3rd Qu.:   0.000   3rd Qu.:42.33   3rd Qu.:  0.4914  
 Max.   :6.000000   Max.   :1834.000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.:  58.0  
 Median : 359.0  
 Mean   : 488.5  
 3rd Qu.: 704.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster03)
   fecha_cnt          tmax            tmin             precip     
 Min.   : 7.00   Min.   :-24.0   Min.   :-110.00   Min.   : 0.00  
 1st Qu.:10.00   1st Qu.:120.0   1st Qu.:  32.00   1st Qu.: 6.00  
 Median :11.00   Median :156.0   Median :  64.00   Median :15.00  
 Mean   :11.02   Mean   :151.7   Mean   :  60.49   Mean   :18.53  
 3rd Qu.:12.00   3rd Qu.:186.0   3rd Qu.:  91.00   3rd Qu.:28.00  
 Max.   :12.00   Max.   :263.0   Max.   : 165.00   Max.   :64.00  
     nevada            prof_nieve          longitud        latitud        
 Min.   :0.0000000   Min.   :  0.0000   Min.   :27.92   Min.   :-17.7550  
 1st Qu.:0.0000000   1st Qu.:  0.0000   1st Qu.:39.49   1st Qu.: -4.8500  
 Median :0.0000000   Median :  0.0000   Median :41.11   Median : -2.6544  
 Mean   :0.0004787   Mean   :  0.1972   Mean   :40.49   Mean   : -2.7096  
 3rd Qu.:0.0000000   3rd Qu.:  0.0000   3rd Qu.:42.29   3rd Qu.:  0.5706  
 Max.   :3.0000000   Max.   :338.0000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.:  71.0  
 Median : 445.0  
 Mean   : 548.7  
 3rd Qu.: 779.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster04)
   fecha_cnt          tmax            tmin            precip           nevada 
 Min.   : 1.00   Min.   :-15.0   Min.   :-75.00   Min.   : 52.00   Min.   :0  
 1st Qu.:10.00   1st Qu.:117.0   1st Qu.: 52.00   1st Qu.: 70.00   1st Qu.:0  
 Median :11.00   Median :142.0   Median : 76.00   Median : 88.50   Median :0  
 Mean   :10.42   Mean   :138.1   Mean   : 71.59   Mean   : 94.75   Mean   :0  
 3rd Qu.:12.00   3rd Qu.:164.0   3rd Qu.: 96.00   3rd Qu.:112.00   3rd Qu.:0  
 Max.   :12.00   Max.   :326.0   Max.   :211.00   Max.   :221.00   Max.   :0  
   prof_nieve         longitud        latitud           altitud      
 Min.   :  0.000   Min.   :27.82   Min.   :-17.889   Min.   :   1.0  
 1st Qu.:  0.000   1st Qu.:40.78   1st Qu.: -8.411   1st Qu.:  44.0  
 Median :  0.000   Median :42.44   Median : -4.010   Median : 251.0  
 Mean   :  1.994   Mean   :41.45   Mean   : -4.537   Mean   : 492.4  
 3rd Qu.:  0.000   3rd Qu.:43.31   3rd Qu.: -1.787   3rd Qu.: 424.0  
 Max.   :607.000   Max.   :43.57   Max.   :  4.216   Max.   :2535.0  
if (!empty_nodes) summary(df.cluster05)
   fecha_cnt           tmax            tmin            precip          nevada 
 Min.   : 1.000   Min.   : 37.0   Min.   :-11.00   Min.   :209.0   Min.   :0  
 1st Qu.: 2.000   1st Qu.:115.0   1st Qu.: 63.00   1st Qu.:226.2   1st Qu.:0  
 Median : 3.000   Median :127.0   Median : 75.00   Median :237.0   Median :0  
 Mean   : 5.462   Mean   :157.7   Mean   : 90.54   Mean   :259.3   Mean   :0  
 3rd Qu.: 8.750   3rd Qu.:154.5   3rd Qu.: 91.50   3rd Qu.:279.8   3rd Qu.:0  
 Max.   :12.000   Max.   :350.0   Max.   :223.00   Max.   :422.0   Max.   :0  
   prof_nieve    longitud        latitud            altitud      
 Min.   :0    Min.   :28.31   Min.   :-16.4992   Min.   :   4.0  
 1st Qu.:0    1st Qu.:41.67   1st Qu.: -8.6239   1st Qu.: 261.0  
 Median :0    Median :42.24   Median : -8.4106   Median : 262.0  
 Mean   :0    Mean   :40.85   Mean   : -7.0646   Mean   : 441.1  
 3rd Qu.:0    3rd Qu.:42.77   3rd Qu.: -3.7807   3rd Qu.: 370.0  
 Max.   :0    Max.   :43.36   Max.   :  0.3181   Max.   :2371.0  

NĂºmero de elementos en cada clĂºster

if (!empty_nodes) {
  df.clusters.dim <- c(dim(df.cluster01)[1], dim(df.cluster02)[1], dim(df.cluster03)[1], dim(df.cluster04)[1], dim(df.cluster05)[1])
  barplot(df.clusters.dim,
          names.arg = c("cluster01", "cluster02", "cluster03", "cluster04", "cluster05"),
          col = "steelblue1")
}

DistribuciĂ³n de los datos

if (!empty_nodes) mpr.hist(df.cluster01)

if (!empty_nodes) mpr.hist(df.cluster02)

if (!empty_nodes) mpr.hist(df.cluster03)

if (!empty_nodes) mpr.hist(df.cluster04)

if (!empty_nodes) mpr.hist(df.cluster05)

if (!empty_nodes) mpr.boxplot(df.cluster01)
if (!empty_nodes) mpr.boxplot(df.cluster02)

if (!empty_nodes) mpr.boxplot(df.cluster03)

if (!empty_nodes) mpr.boxplot(df.cluster04)

if (!empty_nodes) mpr.boxplot(df.cluster05)

LocalizaciĂ³n geogrĂ¡fica de las estaciones de medida de cada cluster

# Agrupa por longitud y latitud para rellenar el mapa con menos datos.
if (!empty_nodes) {
  df.cluster01.grouped <- mpr.group_by_geo(df.cluster01)
  df.cluster02.grouped <- mpr.group_by_geo(df.cluster02)
  df.cluster03.grouped <- mpr.group_by_geo(df.cluster03)
  df.cluster04.grouped <- mpr.group_by_geo(df.cluster04)
  df.cluster05.grouped <- mpr.group_by_geo(df.cluster05)
}
if (!empty_nodes) mpr.draw_map(spain, df.cluster01.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster02.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster03.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster04.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster05.grouped)

VisualizaciĂ³n de 6 clĂºsteres:

if (!empty_nodes) {
  plot(hac, hang=-1, labels=F)
  rect.hclust(hac, k=6)
}

VisualizaciĂ³n de los clĂºsters en el mapa

A quĂ© clĂºster pertenece cada nodo del mapa de kohonen:

if (!empty_nodes) {
  groups <- cutree(hac, k=6)
  plot(model, type="mapping",
    bgcol=c("steelblue1","sienna1","yellowgreen","red","blue","yellow","purple","green","white","#1f77b4", '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2')[groups],
    shape = "straight", labels = "")
  add.cluster.boundaries(model, clustering=groups)
}

AnĂ¡lisis de las observaciones de cada cluster

if (!empty_nodes) {
  # Asignamos a cada registro su clĂºster
  df$cluster <- groups[model$unit.classif]
}

Nuevos dataframes por cluster

if (!empty_nodes) {
  # Creo nuevos dataframes, uno por cada clĂºster.
  df.cluster01 <- subset(df, cluster==1)
  df.cluster02 <- subset(df, cluster==2)
  df.cluster03 <- subset(df, cluster==3)
  df.cluster04 <- subset(df, cluster==4)
  df.cluster05 <- subset(df, cluster==5)
  df.cluster06 <- subset(df, cluster==6)

  # Extraigo del dataframe las features.
  df.cluster01 <- select(df.cluster01, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster02 <- select(df.cluster02, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster03 <- select(df.cluster03, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster04 <- select(df.cluster04, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster05 <- select(df.cluster05, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster06 <- select(df.cluster06, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
}
if (!empty_nodes) summary(df.cluster01)
   fecha_cnt           tmax            tmin           precip          nevada 
 Min.   : 1.000   Min.   :158.0   Min.   : 54.0   Min.   : 0.00   Min.   :0  
 1st Qu.: 6.000   1st Qu.:239.0   1st Qu.:135.0   1st Qu.: 1.00   1st Qu.:0  
 Median : 7.000   Median :267.0   Median :157.0   Median : 5.00   Median :0  
 Mean   : 7.428   Mean   :268.3   Mean   :158.6   Mean   : 9.33   Mean   :0  
 3rd Qu.: 9.000   3rd Qu.:296.0   3rd Qu.:182.0   3rd Qu.:13.00   3rd Qu.:0  
 Max.   :12.000   Max.   :403.0   Max.   :254.0   Max.   :91.00   Max.   :0  
   prof_nieve          longitud        latitud            altitud      
 Min.   : 0.00000   Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.: 0.00000   1st Qu.:37.19   1st Qu.: -6.0556   1st Qu.:  32.0  
 Median : 0.00000   Median :39.95   Median : -3.6781   Median :  87.0  
 Mean   : 0.00356   Mean   :38.56   Mean   : -4.2132   Mean   : 287.8  
 3rd Qu.: 0.00000   3rd Qu.:41.65   3rd Qu.:  0.3664   3rd Qu.: 540.0  
 Max.   :35.00000   Max.   :43.57   Max.   :  4.2156   Max.   :2371.0  
if (!empty_nodes) summary(df.cluster02)
   fecha_cnt          tmax            tmin            precip     
 Min.   :1.000   Min.   : 71.0   Min.   :-40.00   Min.   : 0.00  
 1st Qu.:2.000   1st Qu.:157.0   1st Qu.: 54.00   1st Qu.: 5.00  
 Median :3.000   Median :176.0   Median : 75.00   Median :12.00  
 Mean   :3.408   Mean   :178.4   Mean   : 73.33   Mean   :15.03  
 3rd Qu.:4.000   3rd Qu.:199.0   3rd Qu.: 95.00   3rd Qu.:22.00  
 Max.   :8.000   Max.   :278.0   Max.   :180.00   Max.   :64.00  
     nevada           prof_nieve          longitud        latitud        
 Min.   :0.00e+00   Min.   : 0.00000   Min.   :27.82   Min.   :-17.8889  
 1st Qu.:0.00e+00   1st Qu.: 0.00000   1st Qu.:38.28   1st Qu.: -5.6000  
 Median :0.00e+00   Median : 0.00000   Median :40.82   Median : -2.9056  
 Mean   :8.31e-05   Mean   : 0.05414   Mean   :39.96   Mean   : -2.9246  
 3rd Qu.:0.00e+00   3rd Qu.: 0.00000   3rd Qu.:41.99   3rd Qu.:  0.4914  
 Max.   :2.00e+00   Max.   :83.00000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.:  42.0  
 Median : 169.0  
 Mean   : 366.4  
 3rd Qu.: 627.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster03)
   fecha_cnt          tmax            tmin             precip     
 Min.   : 7.00   Min.   :-24.0   Min.   :-110.00   Min.   : 0.00  
 1st Qu.:10.00   1st Qu.:120.0   1st Qu.:  32.00   1st Qu.: 6.00  
 Median :11.00   Median :156.0   Median :  64.00   Median :15.00  
 Mean   :11.02   Mean   :151.7   Mean   :  60.49   Mean   :18.53  
 3rd Qu.:12.00   3rd Qu.:186.0   3rd Qu.:  91.00   3rd Qu.:28.00  
 Max.   :12.00   Max.   :263.0   Max.   : 165.00   Max.   :64.00  
     nevada            prof_nieve          longitud        latitud        
 Min.   :0.0000000   Min.   :  0.0000   Min.   :27.92   Min.   :-17.7550  
 1st Qu.:0.0000000   1st Qu.:  0.0000   1st Qu.:39.49   1st Qu.: -4.8500  
 Median :0.0000000   Median :  0.0000   Median :41.11   Median : -2.6544  
 Mean   :0.0004787   Mean   :  0.1972   Mean   :40.49   Mean   : -2.7096  
 3rd Qu.:0.0000000   3rd Qu.:  0.0000   3rd Qu.:42.29   3rd Qu.:  0.5706  
 Max.   :3.0000000   Max.   :338.0000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.:  71.0  
 Median : 445.0  
 Mean   : 548.7  
 3rd Qu.: 779.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster04)
   fecha_cnt          tmax            tmin             precip      
 Min.   :1.000   Min.   :-53.0   Min.   :-121.00   Min.   :  0.00  
 1st Qu.:1.000   1st Qu.: 87.0   1st Qu.:  -4.00   1st Qu.:  9.00  
 Median :2.000   Median :110.0   Median :  20.00   Median : 20.00  
 Mean   :2.011   Mean   :105.8   Mean   :  18.93   Mean   : 27.27  
 3rd Qu.:3.000   3rd Qu.:128.0   3rd Qu.:  41.00   3rd Qu.: 39.00  
 Max.   :8.000   Max.   :239.0   Max.   : 172.00   Max.   :126.00  
     nevada           prof_nieve          longitud        latitud        
 Min.   :0.000000   Min.   :   0.000   Min.   :27.82   Min.   :-17.8889  
 1st Qu.:0.000000   1st Qu.:   0.000   1st Qu.:40.66   1st Qu.: -4.8458  
 Median :0.000000   Median :   0.000   Median :41.68   Median : -3.1742  
 Mean   :0.001448   Mean   :   3.079   Mean   :41.15   Mean   : -2.9253  
 3rd Qu.:0.000000   3rd Qu.:   0.000   3rd Qu.:42.53   3rd Qu.:  0.4731  
 Max.   :6.000000   Max.   :1834.000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.: 261.0  
 Median : 617.0  
 Mean   : 738.7  
 3rd Qu.: 916.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster05)
   fecha_cnt          tmax            tmin            precip           nevada 
 Min.   : 1.00   Min.   :-15.0   Min.   :-75.00   Min.   : 52.00   Min.   :0  
 1st Qu.:10.00   1st Qu.:117.0   1st Qu.: 52.00   1st Qu.: 70.00   1st Qu.:0  
 Median :11.00   Median :142.0   Median : 76.00   Median : 88.50   Median :0  
 Mean   :10.42   Mean   :138.1   Mean   : 71.59   Mean   : 94.75   Mean   :0  
 3rd Qu.:12.00   3rd Qu.:164.0   3rd Qu.: 96.00   3rd Qu.:112.00   3rd Qu.:0  
 Max.   :12.00   Max.   :326.0   Max.   :211.00   Max.   :221.00   Max.   :0  
   prof_nieve         longitud        latitud           altitud      
 Min.   :  0.000   Min.   :27.82   Min.   :-17.889   Min.   :   1.0  
 1st Qu.:  0.000   1st Qu.:40.78   1st Qu.: -8.411   1st Qu.:  44.0  
 Median :  0.000   Median :42.44   Median : -4.010   Median : 251.0  
 Mean   :  1.994   Mean   :41.45   Mean   : -4.537   Mean   : 492.4  
 3rd Qu.:  0.000   3rd Qu.:43.31   3rd Qu.: -1.787   3rd Qu.: 424.0  
 Max.   :607.000   Max.   :43.57   Max.   :  4.216   Max.   :2535.0  
if (!empty_nodes) summary(df.cluster06)
   fecha_cnt           tmax            tmin            precip          nevada 
 Min.   : 1.000   Min.   : 37.0   Min.   :-11.00   Min.   :209.0   Min.   :0  
 1st Qu.: 2.000   1st Qu.:115.0   1st Qu.: 63.00   1st Qu.:226.2   1st Qu.:0  
 Median : 3.000   Median :127.0   Median : 75.00   Median :237.0   Median :0  
 Mean   : 5.462   Mean   :157.7   Mean   : 90.54   Mean   :259.3   Mean   :0  
 3rd Qu.: 8.750   3rd Qu.:154.5   3rd Qu.: 91.50   3rd Qu.:279.8   3rd Qu.:0  
 Max.   :12.000   Max.   :350.0   Max.   :223.00   Max.   :422.0   Max.   :0  
   prof_nieve    longitud        latitud            altitud      
 Min.   :0    Min.   :28.31   Min.   :-16.4992   Min.   :   4.0  
 1st Qu.:0    1st Qu.:41.67   1st Qu.: -8.6239   1st Qu.: 261.0  
 Median :0    Median :42.24   Median : -8.4106   Median : 262.0  
 Mean   :0    Mean   :40.85   Mean   : -7.0646   Mean   : 441.1  
 3rd Qu.:0    3rd Qu.:42.77   3rd Qu.: -3.7807   3rd Qu.: 370.0  
 Max.   :0    Max.   :43.36   Max.   :  0.3181   Max.   :2371.0  

NĂºmero de elementos en cada clĂºster

if (!empty_nodes) {
  df.clusters.dim <- c(dim(df.cluster01)[1], dim(df.cluster02)[1], dim(df.cluster03)[1], dim(df.cluster04)[1], dim(df.cluster05)[1], dim(df.cluster06)[1])
  barplot(df.clusters.dim,
          names.arg = c("cluster01", "cluster02", "cluster03", "cluster04", "cluster05", "cluster06"),
          col = "steelblue1")
}

DistribuciĂ³n de los datos

if (!empty_nodes) mpr.hist(df.cluster01)

if (!empty_nodes) mpr.hist(df.cluster02)

if (!empty_nodes) mpr.hist(df.cluster03)

if (!empty_nodes) mpr.hist(df.cluster04)

if (!empty_nodes) mpr.hist(df.cluster05)

if (!empty_nodes) mpr.hist(df.cluster06)

if (!empty_nodes) mpr.boxplot(df.cluster01)
if (!empty_nodes) mpr.boxplot(df.cluster02)

if (!empty_nodes) mpr.boxplot(df.cluster03)

if (!empty_nodes) mpr.boxplot(df.cluster04)

if (!empty_nodes) mpr.boxplot(df.cluster05)

if (!empty_nodes) mpr.boxplot(df.cluster06)

LocalizaciĂ³n geogrĂ¡fica de las estaciones de medida de cada cluster

# Agrupa por longitud y latitud para rellenar el mapa con menos datos.
if (!empty_nodes) {
  df.cluster01.grouped <- mpr.group_by_geo(df.cluster01)
  df.cluster02.grouped <- mpr.group_by_geo(df.cluster02)
  df.cluster03.grouped <- mpr.group_by_geo(df.cluster03)
  df.cluster04.grouped <- mpr.group_by_geo(df.cluster04)
  df.cluster05.grouped <- mpr.group_by_geo(df.cluster05)
  df.cluster06.grouped <- mpr.group_by_geo(df.cluster06)
}
if (!empty_nodes) mpr.draw_map(spain, df.cluster01.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster02.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster03.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster04.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster05.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster06.grouped)

VisualizaciĂ³n de 8 clĂºsteres:

if (!empty_nodes) {
  plot(hac, hang=-1, labels=F)
  rect.hclust(hac, k=8)
}

VisualizaciĂ³n de los clĂºsters en el mapa

A quĂ© clĂºster pertenece cada nodo del mapa de kohonen:

if (!empty_nodes) {
  groups <- cutree(hac, k=8)
  plot(model, type="mapping",
    bgcol=c("steelblue1","sienna1","yellowgreen","red","blue","yellow","purple","green","white","#1f77b4", '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2')[groups],
    shape = "straight", labels = "")
  add.cluster.boundaries(model, clustering=groups)
}

AnĂ¡lisis de las observaciones de cada cluster

if (!empty_nodes) {
  # Asignamos a cada registro su clĂºster
  df$cluster <- groups[model$unit.classif]
}

Nuevos dataframes por cluster

if (!empty_nodes) {
  # Creo nuevos dataframes, uno por cada clĂºster.
  df.cluster01 <- subset(df, cluster==1)
  df.cluster02 <- subset(df, cluster==2)
  df.cluster03 <- subset(df, cluster==3)
  df.cluster04 <- subset(df, cluster==4)
  df.cluster05 <- subset(df, cluster==5)
  df.cluster06 <- subset(df, cluster==6)
  df.cluster07 <- subset(df, cluster==7)
  df.cluster08 <- subset(df, cluster==8)

  # Extraigo del dataframe las features.
  df.cluster01 <- select(df.cluster01, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster02 <- select(df.cluster02, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster03 <- select(df.cluster03, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster04 <- select(df.cluster04, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster05 <- select(df.cluster05, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster06 <- select(df.cluster06, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster07 <- select(df.cluster07, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster08 <- select(df.cluster08, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
}
if (!empty_nodes) summary(df.cluster01)
   fecha_cnt           tmax            tmin           precip          nevada 
 Min.   : 1.000   Min.   :158.0   Min.   : 54.0   Min.   : 0.00   Min.   :0  
 1st Qu.: 6.000   1st Qu.:239.0   1st Qu.:135.0   1st Qu.: 1.00   1st Qu.:0  
 Median : 7.000   Median :267.0   Median :157.0   Median : 5.00   Median :0  
 Mean   : 7.428   Mean   :268.3   Mean   :158.6   Mean   : 9.33   Mean   :0  
 3rd Qu.: 9.000   3rd Qu.:296.0   3rd Qu.:182.0   3rd Qu.:13.00   3rd Qu.:0  
 Max.   :12.000   Max.   :403.0   Max.   :254.0   Max.   :91.00   Max.   :0  
   prof_nieve          longitud        latitud            altitud      
 Min.   : 0.00000   Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.: 0.00000   1st Qu.:37.19   1st Qu.: -6.0556   1st Qu.:  32.0  
 Median : 0.00000   Median :39.95   Median : -3.6781   Median :  87.0  
 Mean   : 0.00356   Mean   :38.56   Mean   : -4.2132   Mean   : 287.8  
 3rd Qu.: 0.00000   3rd Qu.:41.65   3rd Qu.:  0.3664   3rd Qu.: 540.0  
 Max.   :35.00000   Max.   :43.57   Max.   :  4.2156   Max.   :2371.0  
if (!empty_nodes) summary(df.cluster02)
   fecha_cnt          tmax            tmin            precip     
 Min.   :1.000   Min.   : 71.0   Min.   :-40.00   Min.   : 0.00  
 1st Qu.:2.000   1st Qu.:157.0   1st Qu.: 54.00   1st Qu.: 5.00  
 Median :3.000   Median :176.0   Median : 75.00   Median :12.00  
 Mean   :3.408   Mean   :178.4   Mean   : 73.33   Mean   :15.03  
 3rd Qu.:4.000   3rd Qu.:199.0   3rd Qu.: 95.00   3rd Qu.:22.00  
 Max.   :8.000   Max.   :278.0   Max.   :180.00   Max.   :64.00  
     nevada           prof_nieve          longitud        latitud        
 Min.   :0.00e+00   Min.   : 0.00000   Min.   :27.82   Min.   :-17.8889  
 1st Qu.:0.00e+00   1st Qu.: 0.00000   1st Qu.:38.28   1st Qu.: -5.6000  
 Median :0.00e+00   Median : 0.00000   Median :40.82   Median : -2.9056  
 Mean   :8.31e-05   Mean   : 0.05414   Mean   :39.96   Mean   : -2.9246  
 3rd Qu.:0.00e+00   3rd Qu.: 0.00000   3rd Qu.:41.99   3rd Qu.:  0.4914  
 Max.   :2.00e+00   Max.   :83.00000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.:  42.0  
 Median : 169.0  
 Mean   : 366.4  
 3rd Qu.: 627.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster03)
   fecha_cnt          tmax            tmin             precip     
 Min.   : 7.00   Min.   :-24.0   Min.   :-110.00   Min.   : 0.00  
 1st Qu.:10.00   1st Qu.:120.0   1st Qu.:  32.00   1st Qu.: 6.00  
 Median :11.00   Median :156.0   Median :  64.00   Median :15.00  
 Mean   :11.02   Mean   :151.7   Mean   :  60.49   Mean   :18.53  
 3rd Qu.:12.00   3rd Qu.:186.0   3rd Qu.:  91.00   3rd Qu.:28.00  
 Max.   :12.00   Max.   :263.0   Max.   : 165.00   Max.   :64.00  
     nevada            prof_nieve          longitud        latitud        
 Min.   :0.0000000   Min.   :  0.0000   Min.   :27.92   Min.   :-17.7550  
 1st Qu.:0.0000000   1st Qu.:  0.0000   1st Qu.:39.49   1st Qu.: -4.8500  
 Median :0.0000000   Median :  0.0000   Median :41.11   Median : -2.6544  
 Mean   :0.0004787   Mean   :  0.1972   Mean   :40.49   Mean   : -2.7096  
 3rd Qu.:0.0000000   3rd Qu.:  0.0000   3rd Qu.:42.29   3rd Qu.:  0.5706  
 Max.   :3.0000000   Max.   :338.0000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.:  71.0  
 Median : 445.0  
 Mean   : 548.7  
 3rd Qu.: 779.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster04)
   fecha_cnt          tmax             tmin              precip      
 Min.   :1.000   Min.   :-53.00   Min.   :-121.000   Min.   :  0.00  
 1st Qu.:1.000   1st Qu.: 81.00   1st Qu.: -10.000   1st Qu.:  7.00  
 Median :2.000   Median :104.00   Median :  11.000   Median : 15.00  
 Mean   :1.854   Mean   : 98.28   Mean   :   7.905   Mean   : 17.11  
 3rd Qu.:2.000   3rd Qu.:122.00   3rd Qu.:  29.000   3rd Qu.: 24.00  
 Max.   :7.000   Max.   :192.00   Max.   :  75.000   Max.   :126.00  
     nevada           prof_nieve          longitud        latitud       
 Min.   :0.000000   Min.   :   0.000   Min.   :28.31   Min.   :-16.499  
 1st Qu.:0.000000   1st Qu.:   0.000   1st Qu.:40.66   1st Qu.: -4.115  
 Median :0.000000   Median :   0.000   Median :41.52   Median : -2.654  
 Mean   :0.001873   Mean   :   3.785   Mean   :41.03   Mean   : -2.536  
 3rd Qu.:0.000000   3rd Qu.:   0.000   3rd Qu.:42.36   3rd Qu.:  0.595  
 Max.   :6.000000   Max.   :1834.000   Max.   :43.57   Max.   :  4.216  
    altitud      
 Min.   :   1.0  
 1st Qu.: 445.0  
 Median : 687.0  
 Mean   : 827.5  
 3rd Qu.: 990.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster05)
   fecha_cnt          tmax            tmin            precip           nevada 
 Min.   :1.000   Min.   : 56.0   Min.   :-22.00   Min.   : 29.00   Min.   :0  
 1st Qu.:1.000   1st Qu.:111.0   1st Qu.: 36.00   1st Qu.: 47.00   1st Qu.:0  
 Median :2.000   Median :129.0   Median : 56.00   Median : 59.00   Median :0  
 Mean   :2.546   Mean   :131.5   Mean   : 56.55   Mean   : 61.94   Mean   :0  
 3rd Qu.:4.000   3rd Qu.:150.0   3rd Qu.: 75.00   3rd Qu.: 72.00   3rd Qu.:0  
 Max.   :8.000   Max.   :239.0   Max.   :172.00   Max.   :122.00   Max.   :0  
   prof_nieve          longitud        latitud           altitud    
 Min.   :  0.0000   Min.   :27.82   Min.   :-17.889   Min.   :   1  
 1st Qu.:  0.0000   1st Qu.:41.48   1st Qu.: -7.456   1st Qu.:  52  
 Median :  0.0000   Median :42.56   Median : -3.831   Median : 251  
 Mean   :  0.6714   Mean   :41.56   Mean   : -4.255   Mean   : 436  
 3rd Qu.:  0.0000   3rd Qu.:43.31   3rd Qu.: -1.787   3rd Qu.: 510  
 Max.   :666.0000   Max.   :43.57   Max.   :  4.216   Max.   :2535  
if (!empty_nodes) summary(df.cluster06)
   fecha_cnt          tmax            tmin           precip           nevada 
 Min.   : 5.00   Min.   :-15.0   Min.   :-75.0   Min.   : 52.00   Min.   :0  
 1st Qu.:11.00   1st Qu.:119.0   1st Qu.: 54.0   1st Qu.: 68.00   1st Qu.:0  
 Median :11.00   Median :144.0   Median : 77.0   Median : 84.00   Median :0  
 Mean   :10.98   Mean   :139.7   Mean   : 72.6   Mean   : 86.97   Mean   :0  
 3rd Qu.:12.00   3rd Qu.:166.0   3rd Qu.: 97.0   3rd Qu.:100.00   3rd Qu.:0  
 Max.   :12.00   Max.   :326.0   Max.   :211.0   Max.   :155.00   Max.   :0  
   prof_nieve         longitud        latitud           altitud       
 Min.   :  0.000   Min.   :27.82   Min.   :-17.889   Min.   :   1.00  
 1st Qu.:  0.000   1st Qu.:40.78   1st Qu.: -8.372   1st Qu.:  43.15  
 Median :  0.000   Median :42.47   Median : -4.010   Median : 158.00  
 Mean   :  1.554   Mean   :41.48   Mean   : -4.272   Mean   : 494.17  
 3rd Qu.:  0.000   3rd Qu.:43.31   3rd Qu.: -1.787   3rd Qu.: 442.00  
 Max.   :382.000   Max.   :43.57   Max.   :  4.216   Max.   :2535.00  
if (!empty_nodes) summary(df.cluster07)
   fecha_cnt           tmax            tmin            precip          nevada 
 Min.   : 1.000   Min.   : -8.0   Min.   :-58.00   Min.   :123.0   Min.   :0  
 1st Qu.: 2.000   1st Qu.:107.0   1st Qu.: 44.00   1st Qu.:136.0   1st Qu.:0  
 Median : 4.000   Median :127.0   Median : 65.00   Median :150.0   Median :0  
 Mean   : 6.058   Mean   :126.2   Mean   : 63.82   Mean   :154.9   Mean   :0  
 3rd Qu.:11.000   3rd Qu.:149.0   3rd Qu.: 86.00   3rd Qu.:168.0   3rd Qu.:0  
 Max.   :12.000   Max.   :320.0   Max.   :189.00   Max.   :221.0   Max.   :0  
   prof_nieve         longitud        latitud           altitud      
 Min.   :  0.000   Min.   :27.82   Min.   :-17.889   Min.   :   1.0  
 1st Qu.:  0.000   1st Qu.:40.80   1st Qu.: -8.624   1st Qu.:  87.0  
 Median :  0.000   Median :42.24   Median : -8.411   Median : 261.0  
 Mean   :  5.399   Mean   :41.21   Mean   : -6.591   Mean   : 478.4  
 3rd Qu.:  0.000   3rd Qu.:42.89   3rd Qu.: -4.010   3rd Qu.: 370.0  
 Max.   :607.000   Max.   :43.49   Max.   :  2.827   Max.   :2400.0  
if (!empty_nodes) summary(df.cluster08)
   fecha_cnt           tmax            tmin            precip          nevada 
 Min.   : 1.000   Min.   : 37.0   Min.   :-11.00   Min.   :209.0   Min.   :0  
 1st Qu.: 2.000   1st Qu.:115.0   1st Qu.: 63.00   1st Qu.:226.2   1st Qu.:0  
 Median : 3.000   Median :127.0   Median : 75.00   Median :237.0   Median :0  
 Mean   : 5.462   Mean   :157.7   Mean   : 90.54   Mean   :259.3   Mean   :0  
 3rd Qu.: 8.750   3rd Qu.:154.5   3rd Qu.: 91.50   3rd Qu.:279.8   3rd Qu.:0  
 Max.   :12.000   Max.   :350.0   Max.   :223.00   Max.   :422.0   Max.   :0  
   prof_nieve    longitud        latitud            altitud      
 Min.   :0    Min.   :28.31   Min.   :-16.4992   Min.   :   4.0  
 1st Qu.:0    1st Qu.:41.67   1st Qu.: -8.6239   1st Qu.: 261.0  
 Median :0    Median :42.24   Median : -8.4106   Median : 262.0  
 Mean   :0    Mean   :40.85   Mean   : -7.0646   Mean   : 441.1  
 3rd Qu.:0    3rd Qu.:42.77   3rd Qu.: -3.7807   3rd Qu.: 370.0  
 Max.   :0    Max.   :43.36   Max.   :  0.3181   Max.   :2371.0  

NĂºmero de elementos en cada clĂºster

if (!empty_nodes) {
  df.clusters.dim <- c(dim(df.cluster01)[1], dim(df.cluster02)[1], dim(df.cluster03)[1], dim(df.cluster04)[1], dim(df.cluster05)[1], dim(df.cluster06)[1], dim(df.cluster07)[1], dim(df.cluster08)[1])
  barplot(df.clusters.dim,
          names.arg = c("cluster01", "cluster02", "cluster03", "cluster04", "cluster05", "cluster06", "cluster07", "cluster08"),
          col = "steelblue1")
}

DistribuciĂ³n de los datos

if (!empty_nodes) mpr.hist(df.cluster01)

if (!empty_nodes) mpr.hist(df.cluster02)

if (!empty_nodes) mpr.hist(df.cluster03)

if (!empty_nodes) mpr.hist(df.cluster04)

if (!empty_nodes) mpr.hist(df.cluster05)

if (!empty_nodes) mpr.hist(df.cluster06)

if (!empty_nodes) mpr.hist(df.cluster07)

if (!empty_nodes) mpr.hist(df.cluster08)

if (!empty_nodes) mpr.boxplot(df.cluster01)
if (!empty_nodes) mpr.boxplot(df.cluster02)

if (!empty_nodes) mpr.boxplot(df.cluster03)

if (!empty_nodes) mpr.boxplot(df.cluster04)

if (!empty_nodes) mpr.boxplot(df.cluster05)

if (!empty_nodes) mpr.boxplot(df.cluster06)

if (!empty_nodes) mpr.boxplot(df.cluster07)

if (!empty_nodes) mpr.boxplot(df.cluster08)

LocalizaciĂ³n geogrĂ¡fica de las estaciones de medida de cada cluster

# Agrupa por longitud y latitud para rellenar el mapa con menos datos.
if (!empty_nodes) {
  df.cluster01.grouped <- mpr.group_by_geo(df.cluster01)
  df.cluster02.grouped <- mpr.group_by_geo(df.cluster02)
  df.cluster03.grouped <- mpr.group_by_geo(df.cluster03)
  df.cluster04.grouped <- mpr.group_by_geo(df.cluster04)
  df.cluster05.grouped <- mpr.group_by_geo(df.cluster05)
  df.cluster06.grouped <- mpr.group_by_geo(df.cluster06)
  df.cluster07.grouped <- mpr.group_by_geo(df.cluster07)
  df.cluster08.grouped <- mpr.group_by_geo(df.cluster08)
}
if (!empty_nodes) mpr.draw_map(spain, df.cluster01.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster02.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster03.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster04.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster05.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster06.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster07.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster08.grouped)

VisualizaciĂ³n de 10 clĂºsteres:

if (!empty_nodes) {
  plot(hac, hang=-1, labels=F)
  rect.hclust(hac, k=10)
}

VisualizaciĂ³n de los clĂºsters en el mapa

A quĂ© clĂºster pertenece cada nodo del mapa de kohonen:

if (!empty_nodes) {
  groups <- cutree(hac, k=10)
  plot(model, type="mapping",
    bgcol=c("steelblue1","sienna1","yellowgreen","red","blue","yellow","purple","green","white","#1f77b4", '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2')[groups],
    shape = "straight", labels = "")
  add.cluster.boundaries(model, clustering=groups)
}

AnĂ¡lisis de las observaciones de cada cluster

if (!empty_nodes) {
  # Asignamos a cada registro su clĂºster
  df$cluster <- groups[model$unit.classif]
}

Nuevos dataframes por cluster

if (!empty_nodes) {
  # Creo nuevos dataframes, uno por cada clĂºster.
  df.cluster01 <- subset(df, cluster==1)
  df.cluster02 <- subset(df, cluster==2)
  df.cluster03 <- subset(df, cluster==3)
  df.cluster04 <- subset(df, cluster==4)
  df.cluster05 <- subset(df, cluster==5)
  df.cluster06 <- subset(df, cluster==6)
  df.cluster07 <- subset(df, cluster==7)
  df.cluster08 <- subset(df, cluster==8)
  df.cluster09 <- subset(df, cluster==9)
  df.cluster10 <- subset(df, cluster==10)

  # Extraigo del dataframe las features.
  df.cluster01 <- select(df.cluster01, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster02 <- select(df.cluster02, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster03 <- select(df.cluster03, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster04 <- select(df.cluster04, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster05 <- select(df.cluster05, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster06 <- select(df.cluster06, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster07 <- select(df.cluster07, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster08 <- select(df.cluster08, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster09 <- select(df.cluster09, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster10 <- select(df.cluster10, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
}
if (!empty_nodes) summary(df.cluster01)
   fecha_cnt           tmax            tmin           precip           nevada 
 Min.   : 1.000   Min.   :159.0   Min.   : 54.0   Min.   : 0.000   Min.   :0  
 1st Qu.: 6.000   1st Qu.:242.0   1st Qu.:136.0   1st Qu.: 0.000   1st Qu.:0  
 Median : 7.000   Median :270.0   Median :158.0   Median : 4.000   Median :0  
 Mean   : 7.367   Mean   :271.2   Mean   :159.8   Mean   : 6.194   Mean   :0  
 3rd Qu.: 9.000   3rd Qu.:298.0   3rd Qu.:184.0   3rd Qu.:10.000   3rd Qu.:0  
 Max.   :12.000   Max.   :403.0   Max.   :254.0   Max.   :33.000   Max.   :0  
   prof_nieve          longitud        latitud            altitud      
 Min.   : 0.00000   Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.: 0.00000   1st Qu.:37.16   1st Qu.: -6.0556   1st Qu.:  32.0  
 Median : 0.00000   Median :39.85   Median : -3.7642   Median :  87.0  
 Mean   : 0.00159   Mean   :38.32   Mean   : -4.3929   Mean   : 296.1  
 3rd Qu.: 0.00000   3rd Qu.:41.53   3rd Qu.:  0.3664   3rd Qu.: 554.0  
 Max.   :35.00000   Max.   :43.57   Max.   :  4.2156   Max.   :2371.0  
if (!empty_nodes) summary(df.cluster02)
   fecha_cnt          tmax            tmin            precip     
 Min.   :1.000   Min.   : 71.0   Min.   :-40.00   Min.   : 0.00  
 1st Qu.:2.000   1st Qu.:157.0   1st Qu.: 54.00   1st Qu.: 5.00  
 Median :3.000   Median :176.0   Median : 75.00   Median :12.00  
 Mean   :3.408   Mean   :178.4   Mean   : 73.33   Mean   :15.03  
 3rd Qu.:4.000   3rd Qu.:199.0   3rd Qu.: 95.00   3rd Qu.:22.00  
 Max.   :8.000   Max.   :278.0   Max.   :180.00   Max.   :64.00  
     nevada           prof_nieve          longitud        latitud        
 Min.   :0.00e+00   Min.   : 0.00000   Min.   :27.82   Min.   :-17.8889  
 1st Qu.:0.00e+00   1st Qu.: 0.00000   1st Qu.:38.28   1st Qu.: -5.6000  
 Median :0.00e+00   Median : 0.00000   Median :40.82   Median : -2.9056  
 Mean   :8.31e-05   Mean   : 0.05414   Mean   :39.96   Mean   : -2.9246  
 3rd Qu.:0.00e+00   3rd Qu.: 0.00000   3rd Qu.:41.99   3rd Qu.:  0.4914  
 Max.   :2.00e+00   Max.   :83.00000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.:  42.0  
 Median : 169.0  
 Mean   : 366.4  
 3rd Qu.: 627.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster03)
   fecha_cnt           tmax            tmin           precip          nevada 
 Min.   : 4.000   Min.   :158.0   Min.   : 69.0   Min.   :18.00   Min.   :0  
 1st Qu.: 6.000   1st Qu.:217.0   1st Qu.:131.0   1st Qu.:29.00   1st Qu.:0  
 Median : 8.000   Median :237.0   Median :145.0   Median :35.00   Median :0  
 Mean   : 8.035   Mean   :239.6   Mean   :147.4   Mean   :40.22   Mean   :0  
 3rd Qu.: 9.000   3rd Qu.:264.0   3rd Qu.:162.0   3rd Qu.:46.00   3rd Qu.:0  
 Max.   :12.000   Max.   :352.0   Max.   :244.0   Max.   :91.00   Max.   :0  
   prof_nieve          longitud        latitud           altitud      
 Min.   : 0.00000   Min.   :27.82   Min.   :-17.889   Min.   :   1.0  
 1st Qu.: 0.00000   1st Qu.:39.56   1st Qu.: -5.616   1st Qu.:  25.0  
 Median : 0.00000   Median :41.77   Median : -2.039   Median :  81.0  
 Mean   : 0.02296   Mean   :40.88   Mean   : -2.444   Mean   : 206.1  
 3rd Qu.: 0.00000   3rd Qu.:43.31   3rd Qu.:  1.179   3rd Qu.: 261.0  
 Max.   :27.00000   Max.   :43.57   Max.   :  4.216   Max.   :2230.0  
if (!empty_nodes) summary(df.cluster04)
   fecha_cnt          tmax            tmin            precip          nevada 
 Min.   : 7.00   Min.   : 32.0   Min.   :-32.00   Min.   : 0.00   Min.   :0  
 1st Qu.:10.00   1st Qu.:144.0   1st Qu.: 53.00   1st Qu.: 7.00   1st Qu.:0  
 Median :11.00   Median :169.0   Median : 75.00   Median :18.00   Median :0  
 Mean   :10.83   Mean   :167.1   Mean   : 75.03   Mean   :20.77   Mean   :0  
 3rd Qu.:12.00   3rd Qu.:193.0   3rd Qu.: 98.00   3rd Qu.:31.00   3rd Qu.:0  
 Max.   :12.00   Max.   :263.0   Max.   :165.00   Max.   :64.00   Max.   :0  
   prof_nieve          longitud        latitud            altitud      
 Min.   :  0.0000   Min.   :27.92   Min.   :-17.7550   Min.   :   1.0  
 1st Qu.:  0.0000   1st Qu.:38.99   1st Qu.: -5.4981   1st Qu.:  47.0  
 Median :  0.0000   Median :41.11   Median : -2.6544   Median : 263.0  
 Mean   :  0.0863   Mean   :40.36   Mean   : -2.7363   Mean   : 461.8  
 3rd Qu.:  0.0000   3rd Qu.:42.33   3rd Qu.:  0.5706   3rd Qu.: 667.0  
 Max.   :118.0000   Max.   :43.57   Max.   :  4.2156   Max.   :2535.0  
if (!empty_nodes) summary(df.cluster05)
   fecha_cnt          tmax             tmin              precip      
 Min.   :1.000   Min.   :-53.00   Min.   :-121.000   Min.   :  0.00  
 1st Qu.:1.000   1st Qu.: 81.00   1st Qu.: -10.000   1st Qu.:  7.00  
 Median :2.000   Median :104.00   Median :  11.000   Median : 15.00  
 Mean   :1.854   Mean   : 98.28   Mean   :   7.905   Mean   : 17.11  
 3rd Qu.:2.000   3rd Qu.:122.00   3rd Qu.:  29.000   3rd Qu.: 24.00  
 Max.   :7.000   Max.   :192.00   Max.   :  75.000   Max.   :126.00  
     nevada           prof_nieve          longitud        latitud       
 Min.   :0.000000   Min.   :   0.000   Min.   :28.31   Min.   :-16.499  
 1st Qu.:0.000000   1st Qu.:   0.000   1st Qu.:40.66   1st Qu.: -4.115  
 Median :0.000000   Median :   0.000   Median :41.52   Median : -2.654  
 Mean   :0.001873   Mean   :   3.785   Mean   :41.03   Mean   : -2.536  
 3rd Qu.:0.000000   3rd Qu.:   0.000   3rd Qu.:42.36   3rd Qu.:  0.595  
 Max.   :6.000000   Max.   :1834.000   Max.   :43.57   Max.   :  4.216  
    altitud      
 Min.   :   1.0  
 1st Qu.: 445.0  
 Median : 687.0  
 Mean   : 827.5  
 3rd Qu.: 990.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster06)
   fecha_cnt          tmax            tmin            precip           nevada 
 Min.   :1.000   Min.   : 56.0   Min.   :-22.00   Min.   : 29.00   Min.   :0  
 1st Qu.:1.000   1st Qu.:111.0   1st Qu.: 36.00   1st Qu.: 47.00   1st Qu.:0  
 Median :2.000   Median :129.0   Median : 56.00   Median : 59.00   Median :0  
 Mean   :2.546   Mean   :131.5   Mean   : 56.55   Mean   : 61.94   Mean   :0  
 3rd Qu.:4.000   3rd Qu.:150.0   3rd Qu.: 75.00   3rd Qu.: 72.00   3rd Qu.:0  
 Max.   :8.000   Max.   :239.0   Max.   :172.00   Max.   :122.00   Max.   :0  
   prof_nieve          longitud        latitud           altitud    
 Min.   :  0.0000   Min.   :27.82   Min.   :-17.889   Min.   :   1  
 1st Qu.:  0.0000   1st Qu.:41.48   1st Qu.: -7.456   1st Qu.:  52  
 Median :  0.0000   Median :42.56   Median : -3.831   Median : 251  
 Mean   :  0.6714   Mean   :41.56   Mean   : -4.255   Mean   : 436  
 3rd Qu.:  0.0000   3rd Qu.:43.31   3rd Qu.: -1.787   3rd Qu.: 510  
 Max.   :666.0000   Max.   :43.57   Max.   :  4.216   Max.   :2535  
if (!empty_nodes) summary(df.cluster07)
   fecha_cnt          tmax            tmin           precip           nevada 
 Min.   : 5.00   Min.   :-15.0   Min.   :-75.0   Min.   : 52.00   Min.   :0  
 1st Qu.:11.00   1st Qu.:119.0   1st Qu.: 54.0   1st Qu.: 68.00   1st Qu.:0  
 Median :11.00   Median :144.0   Median : 77.0   Median : 84.00   Median :0  
 Mean   :10.98   Mean   :139.7   Mean   : 72.6   Mean   : 86.97   Mean   :0  
 3rd Qu.:12.00   3rd Qu.:166.0   3rd Qu.: 97.0   3rd Qu.:100.00   3rd Qu.:0  
 Max.   :12.00   Max.   :326.0   Max.   :211.0   Max.   :155.00   Max.   :0  
   prof_nieve         longitud        latitud           altitud       
 Min.   :  0.000   Min.   :27.82   Min.   :-17.889   Min.   :   1.00  
 1st Qu.:  0.000   1st Qu.:40.78   1st Qu.: -8.372   1st Qu.:  43.15  
 Median :  0.000   Median :42.47   Median : -4.010   Median : 158.00  
 Mean   :  1.554   Mean   :41.48   Mean   : -4.272   Mean   : 494.17  
 3rd Qu.:  0.000   3rd Qu.:43.31   3rd Qu.: -1.787   3rd Qu.: 442.00  
 Max.   :382.000   Max.   :43.57   Max.   :  4.216   Max.   :2535.00  
if (!empty_nodes) summary(df.cluster08)
   fecha_cnt         tmax             tmin              precip     
 Min.   : 8.0   Min.   :-24.00   Min.   :-110.000   Min.   : 0.00  
 1st Qu.:11.0   1st Qu.: 83.00   1st Qu.:  -6.000   1st Qu.: 4.00  
 Median :12.0   Median : 99.00   Median :  10.000   Median : 8.00  
 Mean   :11.7   Mean   : 94.81   Mean   :   6.715   Mean   :10.26  
 3rd Qu.:12.0   3rd Qu.:112.00   3rd Qu.:  23.000   3rd Qu.:16.00  
 Max.   :12.0   Max.   :171.00   Max.   :  54.000   Max.   :53.00  
     nevada           prof_nieve          longitud        latitud        
 Min.   :0.000000   Min.   :  0.0000   Min.   :28.31   Min.   :-16.4992  
 1st Qu.:0.000000   1st Qu.:  0.0000   1st Qu.:40.66   1st Qu.: -4.1269  
 Median :0.000000   Median :  0.0000   Median :41.51   Median : -2.4831  
 Mean   :0.002249   Mean   :  0.6076   Mean   :40.94   Mean   : -2.6108  
 3rd Qu.:0.000000   3rd Qu.:  0.0000   3rd Qu.:42.08   3rd Qu.:  0.3264  
 Max.   :3.000000   Max.   :338.0000   Max.   :43.43   Max.   :  3.1658  
    altitud      
 Min.   :   2.0  
 1st Qu.: 554.0  
 Median : 775.0  
 Mean   : 870.4  
 3rd Qu.: 990.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster09)
   fecha_cnt           tmax            tmin            precip          nevada 
 Min.   : 1.000   Min.   : -8.0   Min.   :-58.00   Min.   :123.0   Min.   :0  
 1st Qu.: 2.000   1st Qu.:107.0   1st Qu.: 44.00   1st Qu.:136.0   1st Qu.:0  
 Median : 4.000   Median :127.0   Median : 65.00   Median :150.0   Median :0  
 Mean   : 6.058   Mean   :126.2   Mean   : 63.82   Mean   :154.9   Mean   :0  
 3rd Qu.:11.000   3rd Qu.:149.0   3rd Qu.: 86.00   3rd Qu.:168.0   3rd Qu.:0  
 Max.   :12.000   Max.   :320.0   Max.   :189.00   Max.   :221.0   Max.   :0  
   prof_nieve         longitud        latitud           altitud      
 Min.   :  0.000   Min.   :27.82   Min.   :-17.889   Min.   :   1.0  
 1st Qu.:  0.000   1st Qu.:40.80   1st Qu.: -8.624   1st Qu.:  87.0  
 Median :  0.000   Median :42.24   Median : -8.411   Median : 261.0  
 Mean   :  5.399   Mean   :41.21   Mean   : -6.591   Mean   : 478.4  
 3rd Qu.:  0.000   3rd Qu.:42.89   3rd Qu.: -4.010   3rd Qu.: 370.0  
 Max.   :607.000   Max.   :43.49   Max.   :  2.827   Max.   :2400.0  
if (!empty_nodes) summary(df.cluster10)
   fecha_cnt           tmax            tmin            precip          nevada 
 Min.   : 1.000   Min.   : 37.0   Min.   :-11.00   Min.   :209.0   Min.   :0  
 1st Qu.: 2.000   1st Qu.:115.0   1st Qu.: 63.00   1st Qu.:226.2   1st Qu.:0  
 Median : 3.000   Median :127.0   Median : 75.00   Median :237.0   Median :0  
 Mean   : 5.462   Mean   :157.7   Mean   : 90.54   Mean   :259.3   Mean   :0  
 3rd Qu.: 8.750   3rd Qu.:154.5   3rd Qu.: 91.50   3rd Qu.:279.8   3rd Qu.:0  
 Max.   :12.000   Max.   :350.0   Max.   :223.00   Max.   :422.0   Max.   :0  
   prof_nieve    longitud        latitud            altitud      
 Min.   :0    Min.   :28.31   Min.   :-16.4992   Min.   :   4.0  
 1st Qu.:0    1st Qu.:41.67   1st Qu.: -8.6239   1st Qu.: 261.0  
 Median :0    Median :42.24   Median : -8.4106   Median : 262.0  
 Mean   :0    Mean   :40.85   Mean   : -7.0646   Mean   : 441.1  
 3rd Qu.:0    3rd Qu.:42.77   3rd Qu.: -3.7807   3rd Qu.: 370.0  
 Max.   :0    Max.   :43.36   Max.   :  0.3181   Max.   :2371.0  

NĂºmero de elementos en cada clĂºster

if (!empty_nodes) {
  df.clusters.dim <- c(dim(df.cluster01)[1], dim(df.cluster02)[1], dim(df.cluster03)[1], dim(df.cluster04)[1], dim(df.cluster05)[1], dim(df.cluster06)[1], dim(df.cluster07)[1], dim(df.cluster08)[1], dim(df.cluster09)[1], dim(df.cluster10)[1])
  barplot(df.clusters.dim,
          names.arg = c("cluster01", "cluster02", "cluster03", "cluster04", "cluster05", "cluster06", "cluster07", "cluster08", "cluster09", "cluster10"),
          col = "steelblue1")
}

DistribuciĂ³n de los datos

if (!empty_nodes) mpr.hist(df.cluster01)

if (!empty_nodes) mpr.hist(df.cluster02)

if (!empty_nodes) mpr.hist(df.cluster03)

if (!empty_nodes) mpr.hist(df.cluster04)

if (!empty_nodes) mpr.hist(df.cluster05)

if (!empty_nodes) mpr.hist(df.cluster06)

if (!empty_nodes) mpr.hist(df.cluster07)

if (!empty_nodes) mpr.hist(df.cluster08)

if (!empty_nodes) mpr.hist(df.cluster09)

if (!empty_nodes) mpr.hist(df.cluster10)

if (!empty_nodes) mpr.boxplot(df.cluster01)
if (!empty_nodes) mpr.boxplot(df.cluster02)

if (!empty_nodes) mpr.boxplot(df.cluster03)

if (!empty_nodes) mpr.boxplot(df.cluster04)

if (!empty_nodes) mpr.boxplot(df.cluster05)

if (!empty_nodes) mpr.boxplot(df.cluster06)

if (!empty_nodes) mpr.boxplot(df.cluster07)

if (!empty_nodes) mpr.boxplot(df.cluster08)

if (!empty_nodes) mpr.boxplot(df.cluster09)

if (!empty_nodes) mpr.boxplot(df.cluster10)

LocalizaciĂ³n geogrĂ¡fica de las estaciones de medida de cada cluster

# Agrupa por longitud y latitud para rellenar el mapa con menos datos.
if (!empty_nodes) {
  df.cluster01.grouped <- mpr.group_by_geo(df.cluster01)
  df.cluster02.grouped <- mpr.group_by_geo(df.cluster02)
  df.cluster03.grouped <- mpr.group_by_geo(df.cluster03)
  df.cluster04.grouped <- mpr.group_by_geo(df.cluster04)
  df.cluster05.grouped <- mpr.group_by_geo(df.cluster05)
  df.cluster06.grouped <- mpr.group_by_geo(df.cluster06)
  df.cluster07.grouped <- mpr.group_by_geo(df.cluster07)
  df.cluster08.grouped <- mpr.group_by_geo(df.cluster08)
  df.cluster09.grouped <- mpr.group_by_geo(df.cluster09)
  df.cluster10.grouped <- mpr.group_by_geo(df.cluster10)
}
if (!empty_nodes) mpr.draw_map(spain, df.cluster01.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster02.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster03.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster04.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster05.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster06.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster07.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster08.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster09.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster10.grouped)

LS0tCnRpdGxlOiAiQW7DoWxpc2lzIGRlIG1vZGVsb3MgU09NIC0gRnJlY3VlbmNpYSBkYXRvcyBkZSBlbnRyYWRhOiBtZXMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMgTW9kZWxvCgoqIElEOiAzMzEKKiBEZXNjcmlwY2nDs246IAoqIEZyZWN1ZW5jaWE6IG1lcwoqIFZhcmlhYmxlczogZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAKKiBEaW1lbnNpb25lcyBkZWwgbWFwYTogMTAsMTAKKiBJdGVyYWNpb25lczogMTAwMAoqIFBhcsOhbWV0cm9zIGFkaWNpb25hbGVzOiAKCmBgYHtyfQpzb3VyY2UoIi4uLy4uL2xpYi9zb20tdXRpbHMuUiIpCnNvdXJjZSgiLi4vLi4vbGliL21hcHMtdXRpbHMuUiIpCmBgYAoKIyBDYXJnYSBkZWwgbW9kZWxvIGRlc2RlIGRpc2NvCgpgYGB7cn0KbXByLnNldF9iYXNlX3BhdGhfYW5hbHlzaXMoKQptb2RlbCA8LSBtcHIubG9hZF9tb2RlbCgic29tLTMzMS5yZHMueHoiKQpzdW1tYXJ5KG1vZGVsKQpgYGAKCmBgYHtyfQpwbG90KG1vZGVsLCB0eXBlPSJjaGFuZ2VzIikKYGBgCgojIENhcmdhIGRlbCBkYXRhc2V0IGRlIGVudHJhZGEKCmBgYHtyfQpkZiA8LSBtcHIubG9hZF9kYXRhKCJkYXRvc19tZXMuY3N2Lnh6IikKYGBgCgpgYGB7cn0KZGYKYGBgCgpgYGB7cn0Kc3VtbWFyeShkZikKYGBgCgojIENhcmdhIGRlIGxvcyBtYXBhcwoKYGBge3J9CndvcmxkIDwtIG5lX2NvdW50cmllcyhzY2FsZSA9ICJtZWRpdW0iLCByZXR1cm5jbGFzcyA9ICJzZiIpCnNwYWluIDwtIHN1YnNldCh3b3JsZCwgYWRtaW4gPT0gIlNwYWluIikKYGBgCgojIE1hcGEgZGUgZGVuc2lkYWQKCmBgYHtyfQpwbG90KG1vZGVsLCB0eXBlPSJjb3VudCIsIHNoYXBlID0gInN0cmFpZ2h0IiwgcGFsZXR0ZS5uYW1lID0gbXByLmRlZ3JhZGUuYmxldSkKYGBgCgpOw7ptZXJvIGRlIGVsZW1lbnRvcyBlbiBjYWRhIGNlbGRhOgoKYGBge3J9Cm5iIDwtIHRhYmxlKG1vZGVsJHVuaXQuY2xhc3NpZikKcHJpbnQobmIpCmBgYApDb21wcm9iYWNpw7NuIGRlIG5vZG9zIHZhY8Otb3M6CgpgYGB7cn0KZGltX21vZGVsIDwtIDEwKjEwOwpsZW5fbmIgPSBsZW5ndGgobmIpOwplbXB0eV9ub2RlcyA8LSBkaW1fbW9kZWwgIT0gbGVuX25iOwppZiAoZW1wdHlfbm9kZXMpIHsKICBwcmludChwYXN0ZSgiW1dhcm5pbmddIEV4aXN0ZW4gbm9kb3MgdmFjw61vczogIiwgbGVuX25iLCAiLyIsIGRpbV9tb2RlbCkpCn0KYGBgCgojIE1hcGEgZGUgZGlzdGFuY2lhIGVudHJlIHZlY2lub3MKCmBgYHtyfQpwbG90KG1vZGVsLCB0eXBlPSJkaXN0Lm5laWdoYm91cnMiLCBzaGFwZSA9ICJzdHJhaWdodCIpCmBgYAoKIyBJbmZsdWVuY2lhIGRlIGxhcyB2YXJpYWJsZXMKCmBgYHtyfQptb2RlbF9jb2xuYW1lcyA9IGMoImZlY2hhX2NudCIsICJ0bWF4IiwgInRtaW4iLCAicHJlY2lwIikKbW9kZWxfbmNvbCA9IGxlbmd0aChtb2RlbF9jb2xuYW1lcykKYGBgCgojIyBNYXBhIGRlIHZhcmlhYmxlcy4KCmBgYHtyfQpwbG90KG1vZGVsLCBzaGFwZSA9ICJzdHJhaWdodCIpCmBgYAoKIyMgTWFwYSBkZSBjYWxvciBwb3IgdmFyaWFibGUKCmBgYHtyfQpwYXIobWZyb3c9YygzLDQpKQpmb3IgKGogaW4gMTptb2RlbF9uY29sKSB7CiAgcGxvdChtb2RlbCwgdHlwZT0icHJvcGVydHkiLCBwcm9wZXJ0eT1nZXRDb2Rlcyhtb2RlbCwxKVssal0sCiAgICBwYWxldHRlLm5hbWU9bXByLmNvb2xCbHVlSG90UmVkLAogICAgbWFpbj1tb2RlbF9jb2xuYW1lc1tqXSwKICAgIGNleD0wLjUsIHNoYXBlID0gInN0cmFpZ2h0IikKfQpgYGAKCiMjIENvcnJlbGFjacOzbiBwYXJhIGNhZGEgY29sdW1uYSBkZWwgdmVjdG9yIGRlIG5vZG9zCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIGNvciA8LSBhcHBseShnZXRDb2Rlcyhtb2RlbCwxKSwgMiwgbXByLndlaWdodGVkLmNvcnJlbGF0aW9uLCB3PW5iLCBzb209bW9kZWwpCiAgcHJpbnQoY29yKQp9CmBgYAoKUmVwcmVzZW50YWNpw7NuIGRlIGNhZGEgdmFyaWFibGUgZW4gdW4gbWFwYSBkZSBmYWN0b3JlczoKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSB7CiAgcGFyKG1mcm93PWMoMSwxKSkKICBwbG90KGNvclsxLF0sIGNvclsyLF0sIHhsaW09YygtMSwxKSwgeWxpbT1jKC0xLDEpLCB0eXBlPSJuIikKICBsaW5lcyhjKC0xLDEpLGMoMCwwKSkKICBsaW5lcyhjKDAsMCksYygtMSwxKSkKICB0ZXh0KGNvclsxLF0sIGNvclsyLF0sIGxhYmVscz1tb2RlbF9jb2xuYW1lcywgY2V4PTAuNzUpCiAgc3ltYm9scygwLDAsY2lyY2xlcz0xLGluY2hlcz1GLGFkZD1UKQp9CmBgYAoKSW1wb3J0YW5jaWEgZGUgY2FkYSB2YXJpYWJsZSAtIHZhcmlhbnphIHBvbmRlcmFkYSBwb3IgZWwgdGFtYcOxbyBkZSBsYSBjZWxkYToKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSB7CiAgc2lnbWEyIDwtIHNxcnQoYXBwbHkoZ2V0Q29kZXMobW9kZWwsMSksMixmdW5jdGlvbih4LGVmZmVjdGlmKQogICAgIHttPC1zdW0oZWZmZWN0aWYqKHgtd2VpZ2h0ZWQubWVhbih4LGVmZmVjdGlmKSleMikvKHN1bShlZmZlY3RpZiktMSl9LAogICAgIGVmZmVjdGlmPW5iKSkKICBwcmludChzb3J0KHNpZ21hMixkZWNyZWFzaW5nPVQpKQp9CmBgYAoKIyBDbHVzdGVyaW5nCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIGhhYyA8LSBtcHIuaGFjKG1vZGVsLCBuYikKfQpgYGAKCiMjIFZpc3VhbGl6YWNpw7NuIGRlIDMgY2zDunN0ZXJlczoKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSB7CiAgcGxvdChoYWMsIGhhbmc9LTEsIGxhYmVscz1GKQogIHJlY3QuaGNsdXN0KGhhYywgaz0zKQp9CmBgYAoKIyMjIFZpc3VhbGl6YWNpw7NuIGRlIGxvcyBjbMO6c3RlcnMgZW4gZWwgbWFwYQoKQSBxdcOpIGNsw7pzdGVyIHBlcnRlbmVjZSBjYWRhIG5vZG8gZGVsIG1hcGEgZGUga29ob25lbjoKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSB7CiAgZ3JvdXBzIDwtIGN1dHJlZShoYWMsIGs9MykKICBwbG90KG1vZGVsLCB0eXBlPSJtYXBwaW5nIiwKICAgIGJnY29sPWMoInN0ZWVsYmx1ZTEiLCJzaWVubmExIiwieWVsbG93Z3JlZW4iLCJyZWQiLCJibHVlIiwieWVsbG93IiwicHVycGxlIiwiZ3JlZW4iLCJ3aGl0ZSIsIiMxZjc3YjQiLCAnI2ZmN2YwZScsICcjMmNhMDJjJywgJyNkNjI3MjgnLCAnIzk0NjdiZCcsICcjOGM1NjRiJywgJyNlMzc3YzInKVtncm91cHNdLAogICAgc2hhcGUgPSAic3RyYWlnaHQiLCBsYWJlbHMgPSAiIikKICBhZGQuY2x1c3Rlci5ib3VuZGFyaWVzKG1vZGVsLCBjbHVzdGVyaW5nPWdyb3VwcykKfQpgYGAKCiMjIyBBbsOhbGlzaXMgZGUgbGFzIG9ic2VydmFjaW9uZXMgZGUgY2FkYSBjbHVzdGVyCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogICMgQXNpZ25hbW9zIGEgY2FkYSByZWdpc3RybyBzdSBjbMO6c3RlcgogIGRmJGNsdXN0ZXIgPC0gZ3JvdXBzW21vZGVsJHVuaXQuY2xhc3NpZl0KfQpgYGAKCk51ZXZvcyBkYXRhZnJhbWVzIHBvciBjbHVzdGVyCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogICMgQ3JlbyBudWV2b3MgZGF0YWZyYW1lcywgdW5vIHBvciBjYWRhIGNsw7pzdGVyLgogIGRmLmNsdXN0ZXIwMSA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTEpCiAgZGYuY2x1c3RlcjAyIDwtIHN1YnNldChkZiwgY2x1c3Rlcj09MikKICBkZi5jbHVzdGVyMDMgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT0zKQoKICAjIEV4dHJhaWdvIGRlbCBkYXRhZnJhbWUgbGFzIGZlYXR1cmVzLgogIGRmLmNsdXN0ZXIwMSA8LSBzZWxlY3QoZGYuY2x1c3RlcjAxLCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKICBkZi5jbHVzdGVyMDIgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwMiwgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjAzIDwtIHNlbGVjdChkZi5jbHVzdGVyMDMsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQp9CmBgYAoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjAxKQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwMikKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDMpCmBgYAoKIyMjIyBOw7ptZXJvIGRlIGVsZW1lbnRvcyBlbiBjYWRhIGNsw7pzdGVyCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIGRmLmNsdXN0ZXJzLmRpbSA8LSBjKGRpbShkZi5jbHVzdGVyMDEpWzFdLCBkaW0oZGYuY2x1c3RlcjAyKVsxXSwgZGltKGRmLmNsdXN0ZXIwMylbMV0pCiAgYmFycGxvdChkZi5jbHVzdGVycy5kaW0sCiAgICAgICAgICBuYW1lcy5hcmcgPSBjKCJjbHVzdGVyMDEiLCAiY2x1c3RlcjAyIiwgImNsdXN0ZXIwMyIpLAogICAgICAgICAgY29sID0gInN0ZWVsYmx1ZTEiKQp9CmBgYAoKIyMjIyBEaXN0cmlidWNpw7NuIGRlIGxvcyBkYXRvcwoKYGBge3IgZmlnLmhlaWdodD03fQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDEpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwMikKaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjAzKQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9NX0KaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjAxKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDIpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwMykKYGBgCgojIyMgTG9jYWxpemFjacOzbiBnZW9ncsOhZmljYSBkZSBsYXMgZXN0YWNpb25lcyBkZSBtZWRpZGEgZGUgY2FkYSBjbHVzdGVyCgpgYGB7cn0KIyBBZ3J1cGEgcG9yIGxvbmdpdHVkIHkgbGF0aXR1ZCBwYXJhIHJlbGxlbmFyIGVsIG1hcGEgY29uIG1lbm9zIGRhdG9zLgppZiAoIWVtcHR5X25vZGVzKSB7CiAgZGYuY2x1c3RlcjAxLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDEpCiAgZGYuY2x1c3RlcjAyLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDIpCiAgZGYuY2x1c3RlcjAzLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDMpCn0KYGBgCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDEuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDIuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDMuZ3JvdXBlZCkKYGBgCgojIyBWaXN1YWxpemFjacOzbiBkZSA0IGNsw7pzdGVyZXM6CgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIHBsb3QoaGFjLCBoYW5nPS0xLCBsYWJlbHM9RikKICByZWN0LmhjbHVzdChoYWMsIGs9NCkKfQpgYGAKCiMjIyBWaXN1YWxpemFjacOzbiBkZSBsb3MgY2zDunN0ZXJzIGVuIGVsIG1hcGEKCkEgcXXDqSBjbMO6c3RlciBwZXJ0ZW5lY2UgY2FkYSBub2RvIGRlbCBtYXBhIGRlIGtvaG9uZW46CgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIGdyb3VwcyA8LSBjdXRyZWUoaGFjLCBrPTQpCiAgcGxvdChtb2RlbCwgdHlwZT0ibWFwcGluZyIsCiAgICBiZ2NvbD1jKCJzdGVlbGJsdWUxIiwic2llbm5hMSIsInllbGxvd2dyZWVuIiwicmVkIiwiYmx1ZSIsInllbGxvdyIsInB1cnBsZSIsImdyZWVuIiwid2hpdGUiLCIjMWY3N2I0IiwgJyNmZjdmMGUnLCAnIzJjYTAyYycsICcjZDYyNzI4JywgJyM5NDY3YmQnLCAnIzhjNTY0YicsICcjZTM3N2MyJylbZ3JvdXBzXSwKICAgIHNoYXBlID0gInN0cmFpZ2h0IiwgbGFiZWxzID0gIiIpCiAgYWRkLmNsdXN0ZXIuYm91bmRhcmllcyhtb2RlbCwgY2x1c3RlcmluZz1ncm91cHMpCn0KYGBgCgojIyMgQW7DoWxpc2lzIGRlIGxhcyBvYnNlcnZhY2lvbmVzIGRlIGNhZGEgY2x1c3RlcgoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHsKICAjIEFzaWduYW1vcyBhIGNhZGEgcmVnaXN0cm8gc3UgY2zDunN0ZXIKICBkZiRjbHVzdGVyIDwtIGdyb3Vwc1ttb2RlbCR1bml0LmNsYXNzaWZdCn0KYGBgCgpOdWV2b3MgZGF0YWZyYW1lcyBwb3IgY2x1c3RlcgoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHsKICAjIENyZW8gbnVldm9zIGRhdGFmcmFtZXMsIHVubyBwb3IgY2FkYSBjbMO6c3Rlci4KICBkZi5jbHVzdGVyMDEgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT0xKQogIGRmLmNsdXN0ZXIwMiA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTIpCiAgZGYuY2x1c3RlcjAzIDwtIHN1YnNldChkZiwgY2x1c3Rlcj09MykKICBkZi5jbHVzdGVyMDQgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT00KQoKICAjIEV4dHJhaWdvIGRlbCBkYXRhZnJhbWUgbGFzIGZlYXR1cmVzLgogIGRmLmNsdXN0ZXIwMSA8LSBzZWxlY3QoZGYuY2x1c3RlcjAxLCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKICBkZi5jbHVzdGVyMDIgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwMiwgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjAzIDwtIHNlbGVjdChkZi5jbHVzdGVyMDMsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQogIGRmLmNsdXN0ZXIwNCA8LSBzZWxlY3QoZGYuY2x1c3RlcjA0LCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKfQpgYGAKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwMSkKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDIpCmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjAzKQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwNCkKYGBgCgojIyMjIE7Dum1lcm8gZGUgZWxlbWVudG9zIGVuIGNhZGEgY2zDunN0ZXIKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSB7CiAgZGYuY2x1c3RlcnMuZGltIDwtIGMoZGltKGRmLmNsdXN0ZXIwMSlbMV0sIGRpbShkZi5jbHVzdGVyMDIpWzFdLCBkaW0oZGYuY2x1c3RlcjAzKVsxXSwgZGltKGRmLmNsdXN0ZXIwNClbMV0pCiAgYmFycGxvdChkZi5jbHVzdGVycy5kaW0sCiAgICAgICAgICBuYW1lcy5hcmcgPSBjKCJjbHVzdGVyMDEiLCAiY2x1c3RlcjAyIiwgImNsdXN0ZXIwMyIsICJjbHVzdGVyMDQiKSwKICAgICAgICAgIGNvbCA9ICJzdGVlbGJsdWUxIikKfQpgYGAKCiMjIyMgRGlzdHJpYnVjacOzbiBkZSBsb3MgZGF0b3MKCmBgYHtyIGZpZy5oZWlnaHQ9N30KaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjAxKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDIpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwMykKaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjA0KQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9NX0KaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjAxKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDIpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwMykKaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjA0KQpgYGAKCiMjIyBMb2NhbGl6YWNpw7NuIGdlb2dyw6FmaWNhIGRlIGxhcyBlc3RhY2lvbmVzIGRlIG1lZGlkYSBkZSBjYWRhIGNsdXN0ZXIKCmBgYHtyfQojIEFncnVwYSBwb3IgbG9uZ2l0dWQgeSBsYXRpdHVkIHBhcmEgcmVsbGVuYXIgZWwgbWFwYSBjb24gbWVub3MgZGF0b3MuCmlmICghZW1wdHlfbm9kZXMpIHsKICBkZi5jbHVzdGVyMDEuZ3JvdXBlZCA8LSBtcHIuZ3JvdXBfYnlfZ2VvKGRmLmNsdXN0ZXIwMSkKICBkZi5jbHVzdGVyMDIuZ3JvdXBlZCA8LSBtcHIuZ3JvdXBfYnlfZ2VvKGRmLmNsdXN0ZXIwMikKICBkZi5jbHVzdGVyMDMuZ3JvdXBlZCA8LSBtcHIuZ3JvdXBfYnlfZ2VvKGRmLmNsdXN0ZXIwMykKICBkZi5jbHVzdGVyMDQuZ3JvdXBlZCA8LSBtcHIuZ3JvdXBfYnlfZ2VvKGRmLmNsdXN0ZXIwNCkKfQpgYGAKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSBtcHIuZHJhd19tYXAoc3BhaW4sIGRmLmNsdXN0ZXIwMS5ncm91cGVkKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuZHJhd19tYXAoc3BhaW4sIGRmLmNsdXN0ZXIwMi5ncm91cGVkKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuZHJhd19tYXAoc3BhaW4sIGRmLmNsdXN0ZXIwMy5ncm91cGVkKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuZHJhd19tYXAoc3BhaW4sIGRmLmNsdXN0ZXIwNC5ncm91cGVkKQpgYGAKCiMjIFZpc3VhbGl6YWNpw7NuIGRlIDUgY2zDunN0ZXJlczoKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSB7CiAgcGxvdChoYWMsIGhhbmc9LTEsIGxhYmVscz1GKQogIHJlY3QuaGNsdXN0KGhhYywgaz01KQp9CmBgYAoKIyMjIFZpc3VhbGl6YWNpw7NuIGRlIGxvcyBjbMO6c3RlcnMgZW4gZWwgbWFwYQoKQSBxdcOpIGNsw7pzdGVyIHBlcnRlbmVjZSBjYWRhIG5vZG8gZGVsIG1hcGEgZGUga29ob25lbjoKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSB7CiAgZ3JvdXBzIDwtIGN1dHJlZShoYWMsIGs9NSkKICBwbG90KG1vZGVsLCB0eXBlPSJtYXBwaW5nIiwKICAgIGJnY29sPWMoInN0ZWVsYmx1ZTEiLCJzaWVubmExIiwieWVsbG93Z3JlZW4iLCJyZWQiLCJibHVlIiwieWVsbG93IiwicHVycGxlIiwiZ3JlZW4iLCJ3aGl0ZSIsIiMxZjc3YjQiLCAnI2ZmN2YwZScsICcjMmNhMDJjJywgJyNkNjI3MjgnLCAnIzk0NjdiZCcsICcjOGM1NjRiJywgJyNlMzc3YzInKVtncm91cHNdLAogICAgc2hhcGUgPSAic3RyYWlnaHQiLCBsYWJlbHMgPSAiIikKICBhZGQuY2x1c3Rlci5ib3VuZGFyaWVzKG1vZGVsLCBjbHVzdGVyaW5nPWdyb3VwcykKfQpgYGAKCiMjIyBBbsOhbGlzaXMgZGUgbGFzIG9ic2VydmFjaW9uZXMgZGUgY2FkYSBjbHVzdGVyCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogICMgQXNpZ25hbW9zIGEgY2FkYSByZWdpc3RybyBzdSBjbMO6c3RlcgogIGRmJGNsdXN0ZXIgPC0gZ3JvdXBzW21vZGVsJHVuaXQuY2xhc3NpZl0KfQpgYGAKCk51ZXZvcyBkYXRhZnJhbWVzIHBvciBjbHVzdGVyCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogICMgQ3JlbyBudWV2b3MgZGF0YWZyYW1lcywgdW5vIHBvciBjYWRhIGNsw7pzdGVyLgogIGRmLmNsdXN0ZXIwMSA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTEpCiAgZGYuY2x1c3RlcjAyIDwtIHN1YnNldChkZiwgY2x1c3Rlcj09MikKICBkZi5jbHVzdGVyMDMgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT0zKQogIGRmLmNsdXN0ZXIwNCA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTQpCiAgZGYuY2x1c3RlcjA1IDwtIHN1YnNldChkZiwgY2x1c3Rlcj09NSkKCiAgIyBFeHRyYWlnbyBkZWwgZGF0YWZyYW1lIGxhcyBmZWF0dXJlcy4KICBkZi5jbHVzdGVyMDEgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwMSwgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjAyIDwtIHNlbGVjdChkZi5jbHVzdGVyMDIsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQogIGRmLmNsdXN0ZXIwMyA8LSBzZWxlY3QoZGYuY2x1c3RlcjAzLCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKICBkZi5jbHVzdGVyMDQgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwNCwgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjA1IDwtIHNlbGVjdChkZi5jbHVzdGVyMDUsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQp9CmBgYAoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjAxKQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwMikKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDMpCmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjA0KQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwNSkKYGBgCgojIyMjIE7Dum1lcm8gZGUgZWxlbWVudG9zIGVuIGNhZGEgY2zDunN0ZXIKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSB7CiAgZGYuY2x1c3RlcnMuZGltIDwtIGMoZGltKGRmLmNsdXN0ZXIwMSlbMV0sIGRpbShkZi5jbHVzdGVyMDIpWzFdLCBkaW0oZGYuY2x1c3RlcjAzKVsxXSwgZGltKGRmLmNsdXN0ZXIwNClbMV0sIGRpbShkZi5jbHVzdGVyMDUpWzFdKQogIGJhcnBsb3QoZGYuY2x1c3RlcnMuZGltLAogICAgICAgICAgbmFtZXMuYXJnID0gYygiY2x1c3RlcjAxIiwgImNsdXN0ZXIwMiIsICJjbHVzdGVyMDMiLCAiY2x1c3RlcjA0IiwgImNsdXN0ZXIwNSIpLAogICAgICAgICAgY29sID0gInN0ZWVsYmx1ZTEiKQp9CmBgYAoKIyMjIyBEaXN0cmlidWNpw7NuIGRlIGxvcyBkYXRvcwoKYGBge3IgZmlnLmhlaWdodD03fQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDEpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwMikKaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjAzKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwNSkKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTV9CmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwMSkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjAyKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDMpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwNCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjA1KQpgYGAKCiMjIyBMb2NhbGl6YWNpw7NuIGdlb2dyw6FmaWNhIGRlIGxhcyBlc3RhY2lvbmVzIGRlIG1lZGlkYSBkZSBjYWRhIGNsdXN0ZXIKCmBgYHtyfQojIEFncnVwYSBwb3IgbG9uZ2l0dWQgeSBsYXRpdHVkIHBhcmEgcmVsbGVuYXIgZWwgbWFwYSBjb24gbWVub3MgZGF0b3MuCmlmICghZW1wdHlfbm9kZXMpIHsKICBkZi5jbHVzdGVyMDEuZ3JvdXBlZCA8LSBtcHIuZ3JvdXBfYnlfZ2VvKGRmLmNsdXN0ZXIwMSkKICBkZi5jbHVzdGVyMDIuZ3JvdXBlZCA8LSBtcHIuZ3JvdXBfYnlfZ2VvKGRmLmNsdXN0ZXIwMikKICBkZi5jbHVzdGVyMDMuZ3JvdXBlZCA8LSBtcHIuZ3JvdXBfYnlfZ2VvKGRmLmNsdXN0ZXIwMykKICBkZi5jbHVzdGVyMDQuZ3JvdXBlZCA8LSBtcHIuZ3JvdXBfYnlfZ2VvKGRmLmNsdXN0ZXIwNCkKICBkZi5jbHVzdGVyMDUuZ3JvdXBlZCA8LSBtcHIuZ3JvdXBfYnlfZ2VvKGRmLmNsdXN0ZXIwNSkKfQpgYGAKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSBtcHIuZHJhd19tYXAoc3BhaW4sIGRmLmNsdXN0ZXIwMS5ncm91cGVkKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuZHJhd19tYXAoc3BhaW4sIGRmLmNsdXN0ZXIwMi5ncm91cGVkKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuZHJhd19tYXAoc3BhaW4sIGRmLmNsdXN0ZXIwMy5ncm91cGVkKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuZHJhd19tYXAoc3BhaW4sIGRmLmNsdXN0ZXIwNC5ncm91cGVkKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuZHJhd19tYXAoc3BhaW4sIGRmLmNsdXN0ZXIwNS5ncm91cGVkKQpgYGAKCiMjIFZpc3VhbGl6YWNpw7NuIGRlIDYgY2zDunN0ZXJlczoKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSB7CiAgcGxvdChoYWMsIGhhbmc9LTEsIGxhYmVscz1GKQogIHJlY3QuaGNsdXN0KGhhYywgaz02KQp9CmBgYAoKIyMjIFZpc3VhbGl6YWNpw7NuIGRlIGxvcyBjbMO6c3RlcnMgZW4gZWwgbWFwYQoKQSBxdcOpIGNsw7pzdGVyIHBlcnRlbmVjZSBjYWRhIG5vZG8gZGVsIG1hcGEgZGUga29ob25lbjoKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSB7CiAgZ3JvdXBzIDwtIGN1dHJlZShoYWMsIGs9NikKICBwbG90KG1vZGVsLCB0eXBlPSJtYXBwaW5nIiwKICAgIGJnY29sPWMoInN0ZWVsYmx1ZTEiLCJzaWVubmExIiwieWVsbG93Z3JlZW4iLCJyZWQiLCJibHVlIiwieWVsbG93IiwicHVycGxlIiwiZ3JlZW4iLCJ3aGl0ZSIsIiMxZjc3YjQiLCAnI2ZmN2YwZScsICcjMmNhMDJjJywgJyNkNjI3MjgnLCAnIzk0NjdiZCcsICcjOGM1NjRiJywgJyNlMzc3YzInKVtncm91cHNdLAogICAgc2hhcGUgPSAic3RyYWlnaHQiLCBsYWJlbHMgPSAiIikKICBhZGQuY2x1c3Rlci5ib3VuZGFyaWVzKG1vZGVsLCBjbHVzdGVyaW5nPWdyb3VwcykKfQpgYGAKCiMjIyBBbsOhbGlzaXMgZGUgbGFzIG9ic2VydmFjaW9uZXMgZGUgY2FkYSBjbHVzdGVyCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogICMgQXNpZ25hbW9zIGEgY2FkYSByZWdpc3RybyBzdSBjbMO6c3RlcgogIGRmJGNsdXN0ZXIgPC0gZ3JvdXBzW21vZGVsJHVuaXQuY2xhc3NpZl0KfQpgYGAKCk51ZXZvcyBkYXRhZnJhbWVzIHBvciBjbHVzdGVyCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogICMgQ3JlbyBudWV2b3MgZGF0YWZyYW1lcywgdW5vIHBvciBjYWRhIGNsw7pzdGVyLgogIGRmLmNsdXN0ZXIwMSA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTEpCiAgZGYuY2x1c3RlcjAyIDwtIHN1YnNldChkZiwgY2x1c3Rlcj09MikKICBkZi5jbHVzdGVyMDMgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT0zKQogIGRmLmNsdXN0ZXIwNCA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTQpCiAgZGYuY2x1c3RlcjA1IDwtIHN1YnNldChkZiwgY2x1c3Rlcj09NSkKICBkZi5jbHVzdGVyMDYgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT02KQoKICAjIEV4dHJhaWdvIGRlbCBkYXRhZnJhbWUgbGFzIGZlYXR1cmVzLgogIGRmLmNsdXN0ZXIwMSA8LSBzZWxlY3QoZGYuY2x1c3RlcjAxLCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKICBkZi5jbHVzdGVyMDIgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwMiwgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjAzIDwtIHNlbGVjdChkZi5jbHVzdGVyMDMsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQogIGRmLmNsdXN0ZXIwNCA8LSBzZWxlY3QoZGYuY2x1c3RlcjA0LCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKICBkZi5jbHVzdGVyMDUgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwNSwgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjA2IDwtIHNlbGVjdChkZi5jbHVzdGVyMDYsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQp9CmBgYAoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjAxKQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwMikKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDMpCmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjA0KQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwNSkKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDYpCmBgYAoKIyMjIyBOw7ptZXJvIGRlIGVsZW1lbnRvcyBlbiBjYWRhIGNsw7pzdGVyCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIGRmLmNsdXN0ZXJzLmRpbSA8LSBjKGRpbShkZi5jbHVzdGVyMDEpWzFdLCBkaW0oZGYuY2x1c3RlcjAyKVsxXSwgZGltKGRmLmNsdXN0ZXIwMylbMV0sIGRpbShkZi5jbHVzdGVyMDQpWzFdLCBkaW0oZGYuY2x1c3RlcjA1KVsxXSwgZGltKGRmLmNsdXN0ZXIwNilbMV0pCiAgYmFycGxvdChkZi5jbHVzdGVycy5kaW0sCiAgICAgICAgICBuYW1lcy5hcmcgPSBjKCJjbHVzdGVyMDEiLCAiY2x1c3RlcjAyIiwgImNsdXN0ZXIwMyIsICJjbHVzdGVyMDQiLCAiY2x1c3RlcjA1IiwgImNsdXN0ZXIwNiIpLAogICAgICAgICAgY29sID0gInN0ZWVsYmx1ZTEiKQp9CmBgYAoKIyMjIyBEaXN0cmlidWNpw7NuIGRlIGxvcyBkYXRvcwoKYGBge3IgZmlnLmhlaWdodD03fQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDEpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwMikKaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjAzKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwNSkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjA2KQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9NX0KaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjAxKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDIpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwMykKaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjA0KQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDUpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwNikKYGBgCgojIyMgTG9jYWxpemFjacOzbiBnZW9ncsOhZmljYSBkZSBsYXMgZXN0YWNpb25lcyBkZSBtZWRpZGEgZGUgY2FkYSBjbHVzdGVyCgpgYGB7cn0KIyBBZ3J1cGEgcG9yIGxvbmdpdHVkIHkgbGF0aXR1ZCBwYXJhIHJlbGxlbmFyIGVsIG1hcGEgY29uIG1lbm9zIGRhdG9zLgppZiAoIWVtcHR5X25vZGVzKSB7CiAgZGYuY2x1c3RlcjAxLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDEpCiAgZGYuY2x1c3RlcjAyLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDIpCiAgZGYuY2x1c3RlcjAzLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDMpCiAgZGYuY2x1c3RlcjA0Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDQpCiAgZGYuY2x1c3RlcjA1Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDUpCiAgZGYuY2x1c3RlcjA2Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDYpCn0KYGBgCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDEuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDIuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDMuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDQuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDUuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDYuZ3JvdXBlZCkKYGBgCgojIyBWaXN1YWxpemFjacOzbiBkZSA4IGNsw7pzdGVyZXM6CgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIHBsb3QoaGFjLCBoYW5nPS0xLCBsYWJlbHM9RikKICByZWN0LmhjbHVzdChoYWMsIGs9OCkKfQpgYGAKCiMjIyBWaXN1YWxpemFjacOzbiBkZSBsb3MgY2zDunN0ZXJzIGVuIGVsIG1hcGEKCkEgcXXDqSBjbMO6c3RlciBwZXJ0ZW5lY2UgY2FkYSBub2RvIGRlbCBtYXBhIGRlIGtvaG9uZW46CgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIGdyb3VwcyA8LSBjdXRyZWUoaGFjLCBrPTgpCiAgcGxvdChtb2RlbCwgdHlwZT0ibWFwcGluZyIsCiAgICBiZ2NvbD1jKCJzdGVlbGJsdWUxIiwic2llbm5hMSIsInllbGxvd2dyZWVuIiwicmVkIiwiYmx1ZSIsInllbGxvdyIsInB1cnBsZSIsImdyZWVuIiwid2hpdGUiLCIjMWY3N2I0IiwgJyNmZjdmMGUnLCAnIzJjYTAyYycsICcjZDYyNzI4JywgJyM5NDY3YmQnLCAnIzhjNTY0YicsICcjZTM3N2MyJylbZ3JvdXBzXSwKICAgIHNoYXBlID0gInN0cmFpZ2h0IiwgbGFiZWxzID0gIiIpCiAgYWRkLmNsdXN0ZXIuYm91bmRhcmllcyhtb2RlbCwgY2x1c3RlcmluZz1ncm91cHMpCn0KYGBgCgojIyMgQW7DoWxpc2lzIGRlIGxhcyBvYnNlcnZhY2lvbmVzIGRlIGNhZGEgY2x1c3RlcgoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHsKICAjIEFzaWduYW1vcyBhIGNhZGEgcmVnaXN0cm8gc3UgY2zDunN0ZXIKICBkZiRjbHVzdGVyIDwtIGdyb3Vwc1ttb2RlbCR1bml0LmNsYXNzaWZdCn0KYGBgCgpOdWV2b3MgZGF0YWZyYW1lcyBwb3IgY2x1c3RlcgoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHsKICAjIENyZW8gbnVldm9zIGRhdGFmcmFtZXMsIHVubyBwb3IgY2FkYSBjbMO6c3Rlci4KICBkZi5jbHVzdGVyMDEgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT0xKQogIGRmLmNsdXN0ZXIwMiA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTIpCiAgZGYuY2x1c3RlcjAzIDwtIHN1YnNldChkZiwgY2x1c3Rlcj09MykKICBkZi5jbHVzdGVyMDQgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT00KQogIGRmLmNsdXN0ZXIwNSA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTUpCiAgZGYuY2x1c3RlcjA2IDwtIHN1YnNldChkZiwgY2x1c3Rlcj09NikKICBkZi5jbHVzdGVyMDcgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT03KQogIGRmLmNsdXN0ZXIwOCA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTgpCgogICMgRXh0cmFpZ28gZGVsIGRhdGFmcmFtZSBsYXMgZmVhdHVyZXMuCiAgZGYuY2x1c3RlcjAxIDwtIHNlbGVjdChkZi5jbHVzdGVyMDEsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQogIGRmLmNsdXN0ZXIwMiA8LSBzZWxlY3QoZGYuY2x1c3RlcjAyLCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKICBkZi5jbHVzdGVyMDMgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwMywgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjA0IDwtIHNlbGVjdChkZi5jbHVzdGVyMDQsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQogIGRmLmNsdXN0ZXIwNSA8LSBzZWxlY3QoZGYuY2x1c3RlcjA1LCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKICBkZi5jbHVzdGVyMDYgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwNiwgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjA3IDwtIHNlbGVjdChkZi5jbHVzdGVyMDcsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQogIGRmLmNsdXN0ZXIwOCA8LSBzZWxlY3QoZGYuY2x1c3RlcjA4LCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKfQpgYGAKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwMSkKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDIpCmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjAzKQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwNCkKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDUpCmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjA2KQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwNykKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDgpCmBgYAoKIyMjIyBOw7ptZXJvIGRlIGVsZW1lbnRvcyBlbiBjYWRhIGNsw7pzdGVyCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIGRmLmNsdXN0ZXJzLmRpbSA8LSBjKGRpbShkZi5jbHVzdGVyMDEpWzFdLCBkaW0oZGYuY2x1c3RlcjAyKVsxXSwgZGltKGRmLmNsdXN0ZXIwMylbMV0sIGRpbShkZi5jbHVzdGVyMDQpWzFdLCBkaW0oZGYuY2x1c3RlcjA1KVsxXSwgZGltKGRmLmNsdXN0ZXIwNilbMV0sIGRpbShkZi5jbHVzdGVyMDcpWzFdLCBkaW0oZGYuY2x1c3RlcjA4KVsxXSkKICBiYXJwbG90KGRmLmNsdXN0ZXJzLmRpbSwKICAgICAgICAgIG5hbWVzLmFyZyA9IGMoImNsdXN0ZXIwMSIsICJjbHVzdGVyMDIiLCAiY2x1c3RlcjAzIiwgImNsdXN0ZXIwNCIsICJjbHVzdGVyMDUiLCAiY2x1c3RlcjA2IiwgImNsdXN0ZXIwNyIsICJjbHVzdGVyMDgiKSwKICAgICAgICAgIGNvbCA9ICJzdGVlbGJsdWUxIikKfQpgYGAKCiMjIyMgRGlzdHJpYnVjacOzbiBkZSBsb3MgZGF0b3MKCmBgYHtyIGZpZy5oZWlnaHQ9N30KaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjAxKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDIpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwMykKaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjA0KQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDUpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwNikKaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjA3KQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDgpCmBgYAoKYGBge3IgZmlnLmhlaWdodD01fQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDEpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwMikKaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjAzKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwNSkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjA2KQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDcpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwOCkKYGBgCgojIyMgTG9jYWxpemFjacOzbiBnZW9ncsOhZmljYSBkZSBsYXMgZXN0YWNpb25lcyBkZSBtZWRpZGEgZGUgY2FkYSBjbHVzdGVyCgpgYGB7cn0KIyBBZ3J1cGEgcG9yIGxvbmdpdHVkIHkgbGF0aXR1ZCBwYXJhIHJlbGxlbmFyIGVsIG1hcGEgY29uIG1lbm9zIGRhdG9zLgppZiAoIWVtcHR5X25vZGVzKSB7CiAgZGYuY2x1c3RlcjAxLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDEpCiAgZGYuY2x1c3RlcjAyLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDIpCiAgZGYuY2x1c3RlcjAzLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDMpCiAgZGYuY2x1c3RlcjA0Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDQpCiAgZGYuY2x1c3RlcjA1Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDUpCiAgZGYuY2x1c3RlcjA2Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDYpCiAgZGYuY2x1c3RlcjA3Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDcpCiAgZGYuY2x1c3RlcjA4Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDgpCn0KYGBgCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDEuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDIuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDMuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDQuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDUuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDYuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDcuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDguZ3JvdXBlZCkKYGBgCgojIyBWaXN1YWxpemFjacOzbiBkZSAxMCBjbMO6c3RlcmVzOgoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHsKICBwbG90KGhhYywgaGFuZz0tMSwgbGFiZWxzPUYpCiAgcmVjdC5oY2x1c3QoaGFjLCBrPTEwKQp9CmBgYAoKIyMjIFZpc3VhbGl6YWNpw7NuIGRlIGxvcyBjbMO6c3RlcnMgZW4gZWwgbWFwYQoKQSBxdcOpIGNsw7pzdGVyIHBlcnRlbmVjZSBjYWRhIG5vZG8gZGVsIG1hcGEgZGUga29ob25lbjoKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSB7CiAgZ3JvdXBzIDwtIGN1dHJlZShoYWMsIGs9MTApCiAgcGxvdChtb2RlbCwgdHlwZT0ibWFwcGluZyIsCiAgICBiZ2NvbD1jKCJzdGVlbGJsdWUxIiwic2llbm5hMSIsInllbGxvd2dyZWVuIiwicmVkIiwiYmx1ZSIsInllbGxvdyIsInB1cnBsZSIsImdyZWVuIiwid2hpdGUiLCIjMWY3N2I0IiwgJyNmZjdmMGUnLCAnIzJjYTAyYycsICcjZDYyNzI4JywgJyM5NDY3YmQnLCAnIzhjNTY0YicsICcjZTM3N2MyJylbZ3JvdXBzXSwKICAgIHNoYXBlID0gInN0cmFpZ2h0IiwgbGFiZWxzID0gIiIpCiAgYWRkLmNsdXN0ZXIuYm91bmRhcmllcyhtb2RlbCwgY2x1c3RlcmluZz1ncm91cHMpCn0KYGBgCgojIyMgQW7DoWxpc2lzIGRlIGxhcyBvYnNlcnZhY2lvbmVzIGRlIGNhZGEgY2x1c3RlcgoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHsKICAjIEFzaWduYW1vcyBhIGNhZGEgcmVnaXN0cm8gc3UgY2zDunN0ZXIKICBkZiRjbHVzdGVyIDwtIGdyb3Vwc1ttb2RlbCR1bml0LmNsYXNzaWZdCn0KYGBgCgpOdWV2b3MgZGF0YWZyYW1lcyBwb3IgY2x1c3RlcgoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHsKICAjIENyZW8gbnVldm9zIGRhdGFmcmFtZXMsIHVubyBwb3IgY2FkYSBjbMO6c3Rlci4KICBkZi5jbHVzdGVyMDEgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT0xKQogIGRmLmNsdXN0ZXIwMiA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTIpCiAgZGYuY2x1c3RlcjAzIDwtIHN1YnNldChkZiwgY2x1c3Rlcj09MykKICBkZi5jbHVzdGVyMDQgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT00KQogIGRmLmNsdXN0ZXIwNSA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTUpCiAgZGYuY2x1c3RlcjA2IDwtIHN1YnNldChkZiwgY2x1c3Rlcj09NikKICBkZi5jbHVzdGVyMDcgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT03KQogIGRmLmNsdXN0ZXIwOCA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTgpCiAgZGYuY2x1c3RlcjA5IDwtIHN1YnNldChkZiwgY2x1c3Rlcj09OSkKICBkZi5jbHVzdGVyMTAgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT0xMCkKCiAgIyBFeHRyYWlnbyBkZWwgZGF0YWZyYW1lIGxhcyBmZWF0dXJlcy4KICBkZi5jbHVzdGVyMDEgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwMSwgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjAyIDwtIHNlbGVjdChkZi5jbHVzdGVyMDIsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQogIGRmLmNsdXN0ZXIwMyA8LSBzZWxlY3QoZGYuY2x1c3RlcjAzLCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKICBkZi5jbHVzdGVyMDQgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwNCwgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjA1IDwtIHNlbGVjdChkZi5jbHVzdGVyMDUsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQogIGRmLmNsdXN0ZXIwNiA8LSBzZWxlY3QoZGYuY2x1c3RlcjA2LCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKICBkZi5jbHVzdGVyMDcgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwNywgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjA4IDwtIHNlbGVjdChkZi5jbHVzdGVyMDgsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQogIGRmLmNsdXN0ZXIwOSA8LSBzZWxlY3QoZGYuY2x1c3RlcjA5LCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKICBkZi5jbHVzdGVyMTAgPC0gc2VsZWN0KGRmLmNsdXN0ZXIxMCwgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCn0KYGBgCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDEpCmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjAyKQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwMykKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDQpCmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjA1KQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwNikKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDcpCmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjA4KQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwOSkKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMTApCmBgYAoKIyMjIyBOw7ptZXJvIGRlIGVsZW1lbnRvcyBlbiBjYWRhIGNsw7pzdGVyCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIGRmLmNsdXN0ZXJzLmRpbSA8LSBjKGRpbShkZi5jbHVzdGVyMDEpWzFdLCBkaW0oZGYuY2x1c3RlcjAyKVsxXSwgZGltKGRmLmNsdXN0ZXIwMylbMV0sIGRpbShkZi5jbHVzdGVyMDQpWzFdLCBkaW0oZGYuY2x1c3RlcjA1KVsxXSwgZGltKGRmLmNsdXN0ZXIwNilbMV0sIGRpbShkZi5jbHVzdGVyMDcpWzFdLCBkaW0oZGYuY2x1c3RlcjA4KVsxXSwgZGltKGRmLmNsdXN0ZXIwOSlbMV0sIGRpbShkZi5jbHVzdGVyMTApWzFdKQogIGJhcnBsb3QoZGYuY2x1c3RlcnMuZGltLAogICAgICAgICAgbmFtZXMuYXJnID0gYygiY2x1c3RlcjAxIiwgImNsdXN0ZXIwMiIsICJjbHVzdGVyMDMiLCAiY2x1c3RlcjA0IiwgImNsdXN0ZXIwNSIsICJjbHVzdGVyMDYiLCAiY2x1c3RlcjA3IiwgImNsdXN0ZXIwOCIsICJjbHVzdGVyMDkiLCAiY2x1c3RlcjEwIiksCiAgICAgICAgICBjb2wgPSAic3RlZWxibHVlMSIpCn0KYGBgCgojIyMjIERpc3RyaWJ1Y2nDs24gZGUgbG9zIGRhdG9zCgpgYGB7ciBmaWcuaGVpZ2h0PTd9CmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwMSkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjAyKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDMpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwNCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjA1KQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDYpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwNykKaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjA4KQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDkpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIxMCkKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTV9CmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwMSkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjAyKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDMpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwNCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjA1KQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDYpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwNykKaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjA4KQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDkpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIxMCkKYGBgCgojIyMgTG9jYWxpemFjacOzbiBnZW9ncsOhZmljYSBkZSBsYXMgZXN0YWNpb25lcyBkZSBtZWRpZGEgZGUgY2FkYSBjbHVzdGVyCgpgYGB7cn0KIyBBZ3J1cGEgcG9yIGxvbmdpdHVkIHkgbGF0aXR1ZCBwYXJhIHJlbGxlbmFyIGVsIG1hcGEgY29uIG1lbm9zIGRhdG9zLgppZiAoIWVtcHR5X25vZGVzKSB7CiAgZGYuY2x1c3RlcjAxLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDEpCiAgZGYuY2x1c3RlcjAyLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDIpCiAgZGYuY2x1c3RlcjAzLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDMpCiAgZGYuY2x1c3RlcjA0Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDQpCiAgZGYuY2x1c3RlcjA1Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDUpCiAgZGYuY2x1c3RlcjA2Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDYpCiAgZGYuY2x1c3RlcjA3Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDcpCiAgZGYuY2x1c3RlcjA4Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDgpCiAgZGYuY2x1c3RlcjA5Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDkpCiAgZGYuY2x1c3RlcjEwLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMTApCn0KYGBgCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDEuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDIuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDMuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDQuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDUuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDYuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDcuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDguZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDkuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMTAuZ3JvdXBlZCkKYGBgCg==